From 0661e1c2a8a00619bfa7424a78395023a0f480cf Mon Sep 17 00:00:00 2001 From: Anastasio <73900974+anastas10s-afk@users.noreply.github.com> Date: Wed, 18 Jan 2023 20:01:30 +0200 Subject: [PATCH 001/466] Enable WebP images in WooCommerce MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WebP files should be able to be imported in WooCommerce, as they've been supported in WordPress core since v5.8. Adding this pull request, based on the following comments: - https://github.com/woocommerce/woocommerce/issues/28998#issuecomment-1131525719 - https://github.com/woocommerce/woocommerce/issues/28998#issuecomment-1128403295 This is a first for me, hope all is in order. Cheers! 🙂 --- .../includes/wc-core-functions.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/plugins/woocommerce/includes/wc-core-functions.php b/plugins/woocommerce/includes/wc-core-functions.php index d4bbe5c944f..06111cc9813 100644 --- a/plugins/woocommerce/includes/wc-core-functions.php +++ b/plugins/woocommerce/includes/wc-core-functions.php @@ -2583,3 +2583,30 @@ function wc_cache_get_multiple( $keys, $group = '', $force = false ) { } return $values; } + +/** + * Enable WebP images in WooCommerce. + * + * WebP files should be able to be imported in WooCommerce, as they've been supported in WordPress core since v5.8. + */ +add_filter( 'woocommerce_rest_allowed_image_mime_types', 'more_mimes_to_exts' ); +function more_mimes_to_exts( $mime_to_ext ){ + $mime_to_ext['webp'] = 'image/webp'; + + return $mime_to_ext; +} + +function wc_rest_allowed_image_mime_types() { + return apply_filters( + 'woocommerce_rest_allowed_image_mime_types', + array( + 'jpg|jpeg|jpe' => 'image/jpeg', + 'gif' => 'image/gif', + 'png' => 'image/png', + 'bmp' => 'image/bmp', + 'tiff|tif' => 'image/tiff', + 'ico' => 'image/x-icon', + 'webp' => 'image/webp', + ) + ); +} From b6ba8b50aad8e482cb33cca960f1c89f3a8da3af Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Mon, 30 Jan 2023 16:40:01 +0530 Subject: [PATCH 002/466] Improve search query performance by preventing left join. --- .../Orders/OrdersTableSearchQuery.php | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableSearchQuery.php b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableSearchQuery.php index 66cacad4dfd..2ae18e4345a 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableSearchQuery.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableSearchQuery.php @@ -63,11 +63,9 @@ class OrdersTableSearchQuery { */ private function generate_join(): string { $orders_table = $this->query->get_table_name( 'orders' ); - $meta_table = $this->query->get_table_name( 'meta' ); $items_table = $this->query->get_table_name( 'items' ); return " - LEFT JOIN $meta_table AS search_query_meta ON search_query_meta.order_id = $orders_table.id LEFT JOIN $items_table AS search_query_items ON search_query_items.order_id = $orders_table.id "; } @@ -81,25 +79,46 @@ class OrdersTableSearchQuery { */ private function generate_where(): string { $where = ''; - $meta_fields = $this->get_meta_fields_to_be_searched(); $possible_order_id = (string) absint( $this->query->get( 's' ) ); + $order_table = $this->query->get_table_name( 'orders' ); // Support the passing of an order ID as the search term. if ( (string) $this->query->get( 's' ) === $possible_order_id ) { - $where = $this->query->get_table_name( 'orders' ) . '.id = ' . $possible_order_id . ' OR '; + $where = "`$order_table`.id = $possible_order_id OR "; } + $meta_sub_query = $this->generate_where_for_meta_table(); + $where .= " - ( - search_query_meta.meta_key IN ( $meta_fields ) - AND search_query_meta.meta_value LIKE $this->search_term - ) - OR search_query_items.order_item_name LIKE $this->search_term + search_query_items.order_item_name LIKE $this->search_term + OR `$order_table`.id IN ( $meta_sub_query ) "; return " ( $where ) "; } + /** + * Generates where clause for meta table. + * + * Note we generate the where clause as a subquery to be used by calling function inside the IN clause. This is against the general wisdom for performance, but in this particular case, a subquery is able to use the order_id-meta_key-meta_value index, which is not possible with a join. + * + * Since it can use the index, which otherwise would not be possible, it is much faster than both LEFT JOIN or SQL_CALC approach that could have been used. + * + * @return string The where clause for meta table. + */ + private function generate_where_for_meta_table(): string { + $meta_table = $this->query->get_table_name( 'meta' ); + $meta_fields = $this->get_meta_fields_to_be_searched(); + return +" +SELECT search_query_meta.order_id +FROM $meta_table as search_query_meta +WHERE search_query_meta.meta_key IN ( $meta_fields ) +AND search_query_meta.meta_value LIKE $this->search_term +GROUP BY search_query_meta.order_id +"; + } + /** * Returns the order meta field keys to be searched. * From 211787bba92455682682c776551e2b5fb75fbed3 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Mon, 30 Jan 2023 16:41:30 +0530 Subject: [PATCH 003/466] Add changelog. --- plugins/woocommerce/changelog/perf-count_orders | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/perf-count_orders diff --git a/plugins/woocommerce/changelog/perf-count_orders b/plugins/woocommerce/changelog/perf-count_orders new file mode 100644 index 00000000000..87af41b515f --- /dev/null +++ b/plugins/woocommerce/changelog/perf-count_orders @@ -0,0 +1,4 @@ +Significance: minor +Type: performance + +Improve search count query performance by avoiding LEFT JOIN in favor of subquery. From 1c451aa09384728ee840af641655e69a49ba5fd6 Mon Sep 17 00:00:00 2001 From: Atanas Penchev Date: Tue, 28 Feb 2023 00:08:32 +0200 Subject: [PATCH 004/466] FlexSlider always uses CSS3 transitions for "slide" animations jQuery transitions easing method are replicated via CSS3 transition-timing-function --- .../legacy/js/flexslider/jquery.flexslider.js | 69 ++++++++++++++----- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js index 852a326f63f..ba0d5be82f0 100755 --- a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js +++ b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js @@ -8,6 +8,46 @@ var focused = true; + // When wishing to use jQuery transitions, we replicate them with CSS3's transition-timing-function instead + // Default options provided by jQuery are "swing" and "linear"; jQuery easing plugin methods: https://easings.net/ + // We do NOT support easeInElastic, easeOutElastic, easeInOutElastic, easeInBounce, easeOutBounce, and easeInOutBounce + var easings = { + swing: 'cubic-bezier(.02, .01, .47, 1)', // https://stackoverflow.com/a/9245729 + linear: 'linear', + + easeInQuad: 'cubic-bezier(0.11, 0, 0.5, 0)', // https://easings.net/#easeInQuad + easeOutQuad: 'cubic-bezier(0.5, 1, 0.89, 1)', // https://easings.net/#easeOutQuad + easeInOutQuad: 'cubic-bezier(0.45, 0, 0.55, 1)', // https://easings.net/#easeInOutQuad + + easeInCubic: 'cubic-bezier(0.32, 0, 0.67, 0)', // https://easings.net/#easeInCubic + easeOutCubic: 'cubic-bezier(0.33, 1, 0.68, 1)', // https://easings.net/#easeOutCubic + easeInOutCubic: 'cubic-bezier(0.65, 0, 0.35, 1)', // https://easings.net/#easeInOutCubic + + easeInQuart: 'cubic-bezier(0.5, 0, 0.75, 0)', // https://easings.net/#easeInQuart + easeOutQuart: 'cubic-bezier(0.25, 1, 0.5, 1)', // https://easings.net/#easeOutQuart + easeInOutQuart: 'cubic-bezier(0.76, 0, 0.24, 1)', // https://easings.net/#easeInOutQuart + + easeInQuint: 'cubic-bezier(0.64, 0, 0.78, 0)', // https://easings.net/#easeInQuint + easeOutQuint: 'cubic-bezier(0.22, 1, 0.36, 1)', // https://easings.net/#easeOutQuint + easeInOutQuint: 'cubic-bezier(0.83, 0, 0.17, 1)', // https://easings.net/#easeInOutQuint + + easeInSine: 'cubic-bezier(0.12, 0, 0.39, 0)', // https://easings.net/#easeInSine + easeOutSine: 'cubic-bezier(0.61, 1, 0.88, 1)', // https://easings.net/#easeOutSine + easeInOutSine: 'cubic-bezier(0.37, 0, 0.63, 1)', // https://easings.net/#easeInOutSine + + easeInExpo: 'cubic-bezier(0.7, 0, 0.84, 0)', // https://easings.net/#easeInExpo + easeOutExpo: 'cubic-bezier(0.16, 1, 0.3, 1)', // https://easings.net/#easeOutExpo + easeInOutExpo: 'cubic-bezier(0.87, 0, 0.13, 1)', // https://easings.net/#easeInOutExpo + + easeInCirc: 'cubic-bezier(0.55, 0, 1, 0.45)', // https://easings.net/#easeInCirc + easeOutCirc: 'cubic-bezier(0, 0.55, 0.45, 1)', // https://easings.net/#easeOutCirc + easeInOutCirc: 'cubic-bezier(0.85, 0, 0.15, 1)', // https://easings.net/#easeInOutCirc + + easeInBack: 'cubic-bezier(0.36, 0, 0.66, -0.56)', // https://easings.net/#easeInBack + easeOutBack: 'cubic-bezier(0.34, 1.56, 0.64, 1)', // https://easings.net/#easeOutBack + easeInOutBack: 'cubic-bezier(0.68, -0.6, 0.32, 1.6)' // https://easings.net/#easeInOutBack + }; + //FlexSlider: Object Instance $.flexslider = function(el, options) { var slider = $(el); @@ -26,6 +66,7 @@ eventType = "click touchend keyup", watchedEvent = "", watchedEventClearTimer, + easing = easings[slider.vars.easing], vertical = slider.vars.direction === "vertical", reverse = slider.vars.reverse, carousel = (slider.vars.itemWidth > 0), @@ -62,8 +103,8 @@ slider.started = false; slider.startTimeout = null; // TOUCH/USECSS: - slider.transitions = !slider.vars.video && !fade && slider.vars.useCSS; - if (slider.transitions) slider.prop = "transform"; + slider.transform = !slider.vars.video && !fade && slider.vars.useCSS; + if (slider.transform) slider.prop = "transform"; slider.isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; slider.ensureAnimationEnd = ''; // CONTROLSCONTAINER: @@ -452,7 +493,7 @@ if ( ! scrolling || Number( new Date() ) - startT > fxms ) { e.preventDefault(); - if (!fade && slider.transitions) { + if (!fade && slider.transform) { if (!slider.vars.animationLoop) { dx = dx/((slider.currentSlide === 0 && dx < 0 || slider.currentSlide === slider.last && dx > 0) ? (Math.abs(dx)/cwidth+2) : 1); } @@ -640,11 +681,12 @@ slideString = (reverse) ? ((slider.count - 1) - target + slider.cloneOffset) * dimension : (target + slider.cloneOffset) * dimension; } slider.setProps(slideString, "", slider.vars.animationSpeed); - if (slider.transitions) { + if (slider.transform) { if (!slider.vars.animationLoop || !slider.atEnd) { slider.animating = false; slider.currentSlide = slider.animatingTo; } + } // Unbind previous transitionEnd events and re-bind new transitionEnd event slider.container.off("transitionend"); @@ -658,12 +700,6 @@ slider.ensureAnimationEnd = setTimeout(function() { slider.wrapup(dimension); }, slider.vars.animationSpeed + 100); - - } else { - slider.container.animate(slider.args, slider.vars.animationSpeed, slider.vars.easing, function(){ - slider.wrapup(dimension); - }); - } } else { // FADE: if (!touch) { slider.slides.eq(slider.currentSlide).css({"zIndex": 1}).animate({"opacity": 0}, slider.vars.animationSpeed, slider.vars.easing); @@ -767,16 +803,17 @@ return (posCalc * ((slider.vars.rtl)?1:-1)) + "px"; }()); - if (slider.transitions) { + if (slider.transform) { target = (vertical) ? "translate3d(0," + target + ",0)" : "translate3d(" + (parseInt(target)+'px') + ",0,0)"; + } else { + // When wishing to use jQuery transitions, we replicate them with CSS3's transition-timing-function instead + slider.container.css("transition-timing-function", easing); + } dur = (dur !== undefined) ? (dur/1000) + "s" : "0s"; slider.container.css("transition-duration", dur); - } slider.args[slider.prop] = target; - if (slider.transitions || dur === undefined) { slider.container.css(slider.args); } - - slider.container.css('transform',target); + slider.container.css(slider.args); }; slider.setup = function(type) { @@ -996,7 +1033,7 @@ namespace: "flex-", //{NEW} String: Prefix string attached to the class of every element generated by the plugin selector: ".slides > li", //{NEW} Selector: Must match a simple pattern. '{container} > {slide}' -- Ignore pattern at your own peril animation: "fade", //String: Select your animation type, "fade" or "slide" - easing: "swing", //{NEW} String: Determines the easing method used in jQuery transitions. jQuery easing plugin is supported! + easing: "swing", //{NEW} String: Determines the easing method used in jQuery transitions. Most jQuery easing plugin methods are supported! direction: "horizontal", //String: Select the sliding direction, "horizontal" or "vertical" reverse: false, //{NEW} Boolean: Reverse the animation direction animationLoop: true, //Boolean: Should the animation loop? If false, directionNav will received "disable" classes at either end From 72695598d1fc2a70ab22c6b75f92a6ef687bf587 Mon Sep 17 00:00:00 2001 From: Atanas Penchev Date: Tue, 28 Feb 2023 00:29:52 +0200 Subject: [PATCH 005/466] Changelog --- plugins/woocommerce/changelog/flexslider-css3-transitions | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/flexslider-css3-transitions diff --git a/plugins/woocommerce/changelog/flexslider-css3-transitions b/plugins/woocommerce/changelog/flexslider-css3-transitions new file mode 100644 index 00000000000..8dd6d32af2a --- /dev/null +++ b/plugins/woocommerce/changelog/flexslider-css3-transitions @@ -0,0 +1,4 @@ +Significance: major +Type: update + +FlexSlider always uses CSS3 transitions for "slide" animations. From caead0385f3d1e9932dad57984d911fd6c984b39 Mon Sep 17 00:00:00 2001 From: Atanas Penchev Date: Sat, 4 Mar 2023 20:19:12 +0200 Subject: [PATCH 006/466] Add fallback easing --- .../client/legacy/js/flexslider/jquery.flexslider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js index ba0d5be82f0..2cdc4acba55 100755 --- a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js +++ b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js @@ -66,7 +66,7 @@ eventType = "click touchend keyup", watchedEvent = "", watchedEventClearTimer, - easing = easings[slider.vars.easing], + easing = easings[slider.vars.easing] || "ease", vertical = slider.vars.direction === "vertical", reverse = slider.vars.reverse, carousel = (slider.vars.itemWidth > 0), From 8caba33649f4ffe80a4075f67c7a86af41757cbd Mon Sep 17 00:00:00 2001 From: Atanas Penchev Date: Sat, 4 Mar 2023 20:22:57 +0200 Subject: [PATCH 007/466] Whitespace --- .../client/legacy/js/flexslider/jquery.flexslider.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js index 2cdc4acba55..3d96abe097a 100755 --- a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js +++ b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js @@ -12,7 +12,7 @@ // Default options provided by jQuery are "swing" and "linear"; jQuery easing plugin methods: https://easings.net/ // We do NOT support easeInElastic, easeOutElastic, easeInOutElastic, easeInBounce, easeOutBounce, and easeInOutBounce var easings = { - swing: 'cubic-bezier(.02, .01, .47, 1)', // https://stackoverflow.com/a/9245729 + swing: 'cubic-bezier(.02, .01, .47, 1)', // https://stackoverflow.com/a/9245729 linear: 'linear', easeInQuad: 'cubic-bezier(0.11, 0, 0.5, 0)', // https://easings.net/#easeInQuad From 75a2615f5a53b5e7b1ea376926a0448335583ab5 Mon Sep 17 00:00:00 2001 From: Atanas Penchev Date: Sat, 4 Mar 2023 20:51:14 +0200 Subject: [PATCH 008/466] Add missing jQuery easing methods --- .../client/legacy/js/flexslider/jquery.flexslider.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js index 3d96abe097a..ea0f390ac26 100755 --- a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js +++ b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js @@ -48,6 +48,9 @@ easeInOutBack: 'cubic-bezier(0.68, -0.6, 0.32, 1.6)' // https://easings.net/#easeInOutBack }; + easings['jswing'] = easings['swing']; // https://github.com/gdsmith/jquery.easing/blob/v1.4.0/jquery.easing.js#L22 + easings['def'] = easings['easeOutQuad']; // https://github.com/gdsmith/jquery.easing/blob/v1.4.0/jquery.easing.js#L52 + //FlexSlider: Object Instance $.flexslider = function(el, options) { var slider = $(el); From 87c40e7715a78f6c9eb27581a7e8fe9838731b77 Mon Sep 17 00:00:00 2001 From: Atanas Penchev Date: Sun, 5 Mar 2023 20:16:25 +0200 Subject: [PATCH 009/466] Correct 'jswing' easing --- .../client/legacy/js/flexslider/jquery.flexslider.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js index ea0f390ac26..25c6098459d 100755 --- a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js +++ b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js @@ -48,8 +48,8 @@ easeInOutBack: 'cubic-bezier(0.68, -0.6, 0.32, 1.6)' // https://easings.net/#easeInOutBack }; - easings['jswing'] = easings['swing']; // https://github.com/gdsmith/jquery.easing/blob/v1.4.0/jquery.easing.js#L22 - easings['def'] = easings['easeOutQuad']; // https://github.com/gdsmith/jquery.easing/blob/v1.4.0/jquery.easing.js#L52 + // Preserve the original jQuery "swing" easing as "jswing" + easings['jswing'] = easings['swing']; //FlexSlider: Object Instance $.flexslider = function(el, options) { From 8e64856bd497a40bcbb975347ca25a3cb63e7d55 Mon Sep 17 00:00:00 2001 From: Atanas Penchev Date: Mon, 6 Mar 2023 13:25:48 +0200 Subject: [PATCH 010/466] Element.animate() instead of CSS3 transitions --- .../legacy/js/flexslider/jquery.flexslider.js | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js index 25c6098459d..e907b50aa35 100755 --- a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js +++ b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js @@ -106,8 +106,8 @@ slider.started = false; slider.startTimeout = null; // TOUCH/USECSS: - slider.transform = !slider.vars.video && !fade && slider.vars.useCSS; - if (slider.transform) slider.prop = "transform"; + slider.transitions = !slider.vars.video && !fade && slider.vars.useCSS; + if (slider.transitions) slider.prop = "transform"; slider.isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; slider.ensureAnimationEnd = ''; // CONTROLSCONTAINER: @@ -496,7 +496,7 @@ if ( ! scrolling || Number( new Date() ) - startT > fxms ) { e.preventDefault(); - if (!fade && slider.transform) { + if (!fade && slider.transitions) { if (!slider.vars.animationLoop) { dx = dx/((slider.currentSlide === 0 && dx < 0 || slider.currentSlide === slider.last && dx > 0) ? (Math.abs(dx)/cwidth+2) : 1); } @@ -684,12 +684,11 @@ slideString = (reverse) ? ((slider.count - 1) - target + slider.cloneOffset) * dimension : (target + slider.cloneOffset) * dimension; } slider.setProps(slideString, "", slider.vars.animationSpeed); - if (slider.transform) { + if (slider.transitions) { if (!slider.vars.animationLoop || !slider.atEnd) { slider.animating = false; slider.currentSlide = slider.animatingTo; } - } // Unbind previous transitionEnd events and re-bind new transitionEnd event slider.container.off("transitionend"); @@ -703,6 +702,19 @@ slider.ensureAnimationEnd = setTimeout(function() { slider.wrapup(dimension); }, slider.vars.animationSpeed + 100); + + } else { + var keyframes = {}; + keyframes[slider.prop] = [ + slider.container.css(slider.prop), + slider.args[slider.prop] + ]; + + slider.container[0].animate(keyframes, { duration: slider.vars.animationSpeed, easing: easing }).onfinish = function() { + slider.container.css(slider.args); + slider.wrapup(dimension); + }; + } } else { // FADE: if (!touch) { slider.slides.eq(slider.currentSlide).css({"zIndex": 1}).animate({"opacity": 0}, slider.vars.animationSpeed, slider.vars.easing); @@ -806,17 +818,16 @@ return (posCalc * ((slider.vars.rtl)?1:-1)) + "px"; }()); - if (slider.transform) { + if (slider.transitions) { target = (vertical) ? "translate3d(0," + target + ",0)" : "translate3d(" + (parseInt(target)+'px') + ",0,0)"; - } else { - // When wishing to use jQuery transitions, we replicate them with CSS3's transition-timing-function instead - slider.container.css("transition-timing-function", easing); - } dur = (dur !== undefined) ? (dur/1000) + "s" : "0s"; slider.container.css("transition-duration", dur); + } slider.args[slider.prop] = target; - slider.container.css(slider.args); + if (slider.transitions || dur === undefined) { slider.container.css(slider.args); } + + slider.container.css('transform',target); }; slider.setup = function(type) { From 9bc08a383e4973b7ff0d4c783d971ac650aafc40 Mon Sep 17 00:00:00 2001 From: Atanas Penchev Date: Tue, 7 Mar 2023 21:48:23 +0200 Subject: [PATCH 011/466] Correct changelog --- plugins/woocommerce/changelog/flexslider-css3-transitions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/changelog/flexslider-css3-transitions b/plugins/woocommerce/changelog/flexslider-css3-transitions index 8dd6d32af2a..1ac28ac7d2b 100644 --- a/plugins/woocommerce/changelog/flexslider-css3-transitions +++ b/plugins/woocommerce/changelog/flexslider-css3-transitions @@ -1,4 +1,4 @@ Significance: major Type: update -FlexSlider always uses CSS3 transitions for "slide" animations. +FlexSlider always uses Web Animations API for "slide" animations. From 65e1d170ba421748ab5d7dd60fb760e62122d510 Mon Sep 17 00:00:00 2001 From: Atanas Penchev Date: Fri, 10 Mar 2023 13:16:18 +0200 Subject: [PATCH 012/466] Handle multiple slider containers --- .../legacy/js/flexslider/jquery.flexslider.js | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js index e907b50aa35..a175fdfa61b 100755 --- a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js +++ b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js @@ -704,16 +704,19 @@ }, slider.vars.animationSpeed + 100); } else { - var keyframes = {}; - keyframes[slider.prop] = [ - slider.container.css(slider.prop), - slider.args[slider.prop] - ]; + slider.container.each(function() { + var container = this; + var keyframes = {}; + keyframes[slider.prop] = [ + window.getComputedStyle(container)[slider.prop], + slider.args[slider.prop] + ]; - slider.container[0].animate(keyframes, { duration: slider.vars.animationSpeed, easing: easing }).onfinish = function() { - slider.container.css(slider.args); - slider.wrapup(dimension); - }; + container.animate(keyframes, { duration: slider.vars.animationSpeed, easing: easing }).onfinish = function() { + container.style[slider.prop] = slider.args[slider.prop]; + slider.wrapup(dimension); + }; + }); } } else { // FADE: if (!touch) { From 6db4d6b68a16ec80544e1c542e3ebf363d8aca48 Mon Sep 17 00:00:00 2001 From: Atanas Penchev Date: Sat, 11 Mar 2023 03:36:23 +0200 Subject: [PATCH 013/466] Extract variable --- .../client/legacy/js/flexslider/jquery.flexslider.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js index a175fdfa61b..c1a941bf7ab 100755 --- a/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js +++ b/plugins/woocommerce/client/legacy/js/flexslider/jquery.flexslider.js @@ -704,16 +704,17 @@ }, slider.vars.animationSpeed + 100); } else { + var prop = slider.prop; slider.container.each(function() { var container = this; var keyframes = {}; - keyframes[slider.prop] = [ - window.getComputedStyle(container)[slider.prop], - slider.args[slider.prop] + keyframes[prop] = [ + window.getComputedStyle(container)[prop], + slider.args[prop] ]; container.animate(keyframes, { duration: slider.vars.animationSpeed, easing: easing }).onfinish = function() { - container.style[slider.prop] = slider.args[slider.prop]; + container.style[prop] = slider.args[prop]; slider.wrapup(dimension); }; }); From 42235d1f31c22e579b1b5cd8205563ad7ffa940d Mon Sep 17 00:00:00 2001 From: Niels Lange Date: Tue, 14 Mar 2023 11:54:12 +0700 Subject: [PATCH 014/466] Update extend-cart-checkout-block dependency --- .../.prettierrc.js.mustache | 3 +++ .../$slug-blocks-integration.php.mustache | 2 +- .../.editorconfig.mustache | 27 +++++++++++++++++++ .../.nvmrc.mustache | 1 + .../js/extend-cart-checkout-block/README.md | 23 +++++++++++++--- ...date-extend-cart-checkout-block-dependency | 10 +++++++ .../js/extend-cart-checkout-block/index.js | 2 ++ .../.wp-env.json.mustache | 14 ++++++++++ .../block.json.mustache | 2 +- .../webpack.config.js.mustache | 3 +-- 10 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 bin/packages/js/extend-cart-checkout-block/.prettierrc.js.mustache create mode 100644 packages/js/extend-cart-checkout-block/.editorconfig.mustache create mode 100644 packages/js/extend-cart-checkout-block/.nvmrc.mustache create mode 100644 packages/js/extend-cart-checkout-block/changelog/update-extend-cart-checkout-block-dependency create mode 100644 packages/js/extend-cart-checkout-block/packages/js/extend-cart-checkout-block/.wp-env.json.mustache diff --git a/bin/packages/js/extend-cart-checkout-block/.prettierrc.js.mustache b/bin/packages/js/extend-cart-checkout-block/.prettierrc.js.mustache new file mode 100644 index 00000000000..51b8aeb4150 --- /dev/null +++ b/bin/packages/js/extend-cart-checkout-block/.prettierrc.js.mustache @@ -0,0 +1,3 @@ +// Import the default config file and expose it in the project root. +// Useful for editor integrations. +module.exports = require( '@wordpress/prettier-config' ); diff --git a/packages/js/extend-cart-checkout-block/$slug-blocks-integration.php.mustache b/packages/js/extend-cart-checkout-block/$slug-blocks-integration.php.mustache index 89762e199dd..88ea6607b6e 100644 --- a/packages/js/extend-cart-checkout-block/$slug-blocks-integration.php.mustache +++ b/packages/js/extend-cart-checkout-block/$slug-blocks-integration.php.mustache @@ -105,7 +105,7 @@ class {{slugPascalCase}}_Blocks_Integration implements IntegrationInterface { $style_url = plugins_url( $style_path, __FILE__ ); wp_enqueue_style( - '{{slug}}-blocks-integration', + '{{slug}}-checkout-newsletter-subscription-block', $style_url, [], $this->get_file_version( $style_path ) diff --git a/packages/js/extend-cart-checkout-block/.editorconfig.mustache b/packages/js/extend-cart-checkout-block/.editorconfig.mustache new file mode 100644 index 00000000000..7b68c1c4230 --- /dev/null +++ b/packages/js/extend-cart-checkout-block/.editorconfig.mustache @@ -0,0 +1,27 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +# WordPress Coding Standards +# https://make.wordpress.org/core/handbook/coding-standards/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +tab_width = 4 +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true + +[*.txt] +trim_trailing_whitespace = false + +[*.{md,json,yml}] +trim_trailing_whitespace = false +indent_style = space +indent_size = 2 + +[*.json] +indent_style = tab diff --git a/packages/js/extend-cart-checkout-block/.nvmrc.mustache b/packages/js/extend-cart-checkout-block/.nvmrc.mustache new file mode 100644 index 00000000000..23d9c36a118 --- /dev/null +++ b/packages/js/extend-cart-checkout-block/.nvmrc.mustache @@ -0,0 +1 @@ +16.13.2 diff --git a/packages/js/extend-cart-checkout-block/README.md b/packages/js/extend-cart-checkout-block/README.md index 287776ad415..40c5523f79c 100644 --- a/packages/js/extend-cart-checkout-block/README.md +++ b/packages/js/extend-cart-checkout-block/README.md @@ -3,12 +3,29 @@ This is a template to be used with `@wordpress/create-block` to create a WooCommerce Blocks extension starting point. ## Installation + From your `plugins` directory run: -``` + +```sh npx @wordpress/create-block -t @woocommerce/extend-cart-checkout-block your_extension_name ``` When this has completed, go to your WordPress plugins page and activate the plugin. -Add some items to your cart and visit the Checkout block, notice there is additional data on the block that this -template has added. +Add some items to your cart and visit the Checkout block, notice there is additional data on the block that this template has added. + +### Installing `wp-prettier` (optional) + +WooCommerce Blocks uses `wp-prettier` to format the JS files. If you want to use `wp-prettier`, you will need to run the following command: + +```sh +nvm use && npm i --D "prettier@npm:wp-prettier@latest" +``` + +### Installing `wp-env` (optional) + +`wp-env` lets you easily set up a local WordPress environment for building and testing your extension. If you want to use `wp-env`, you will need to run the following command: + +```sh +nvm use && npm i -D @wordpress/env && npm set-script wp-env "wp-env" +``` diff --git a/packages/js/extend-cart-checkout-block/changelog/update-extend-cart-checkout-block-dependency b/packages/js/extend-cart-checkout-block/changelog/update-extend-cart-checkout-block-dependency new file mode 100644 index 00000000000..81e8d04ec70 --- /dev/null +++ b/packages/js/extend-cart-checkout-block/changelog/update-extend-cart-checkout-block-dependency @@ -0,0 +1,10 @@ +Significance: patch +Type: update + +This patch includes the following changes: + +- Add `prettier:npm:wp-prettier@2.6.2` +- Add `.wp-env` +- Add `.nvmrc` +- Correct CSS handle of newsletter subscription block +- Correct metadata category of newsletter subscription block diff --git a/packages/js/extend-cart-checkout-block/index.js b/packages/js/extend-cart-checkout-block/index.js index 7273a77eeaa..325e7711b22 100644 --- a/packages/js/extend-cart-checkout-block/index.js +++ b/packages/js/extend-cart-checkout-block/index.js @@ -4,6 +4,8 @@ module.exports = { npmDevDependencies: [ '@woocommerce/dependency-extraction-webpack-plugin', '@woocommerce/eslint-plugin', + '@wordpress/prettier-config', + '@wordpress/scripts', ], }, }; diff --git a/packages/js/extend-cart-checkout-block/packages/js/extend-cart-checkout-block/.wp-env.json.mustache b/packages/js/extend-cart-checkout-block/packages/js/extend-cart-checkout-block/.wp-env.json.mustache new file mode 100644 index 00000000000..ecacceb5895 --- /dev/null +++ b/packages/js/extend-cart-checkout-block/packages/js/extend-cart-checkout-block/.wp-env.json.mustache @@ -0,0 +1,14 @@ +{ + "phpVersion": null, + "core": null, + "plugins": [ + "https://downloads.wordpress.org/plugin/woocommerce.latest-stable.zip", + "https://downloads.wordpress.org/plugin/woo-gutenberg-products-block.latest-stable.zip", + "." + ], + "config": { + "JETPACK_AUTOLOAD_DEV": true, + "WP_DEBUG": true, + "SCRIPT_DEBUG": true + } +} diff --git a/packages/js/extend-cart-checkout-block/src/js/checkout-newsletter-subscription-block/block.json.mustache b/packages/js/extend-cart-checkout-block/src/js/checkout-newsletter-subscription-block/block.json.mustache index ed70cbd7b67..98553e8cae7 100644 --- a/packages/js/extend-cart-checkout-block/src/js/checkout-newsletter-subscription-block/block.json.mustache +++ b/packages/js/extend-cart-checkout-block/src/js/checkout-newsletter-subscription-block/block.json.mustache @@ -3,7 +3,7 @@ "name": "{{slug}}/checkout-newsletter-subscription", "version": "2.0.0", "title": "Newsletter Subscription!", - "category": "{{slug}}", + "category": "woocommerce", "description": "Adds a newsletter subscription checkbox to the checkout.", "supports": { "html": false, diff --git a/packages/js/extend-cart-checkout-block/webpack.config.js.mustache b/packages/js/extend-cart-checkout-block/webpack.config.js.mustache index ce8447b6078..4f850022df0 100644 --- a/packages/js/extend-cart-checkout-block/webpack.config.js.mustache +++ b/packages/js/extend-cart-checkout-block/webpack.config.js.mustache @@ -61,8 +61,7 @@ module.exports = { // Add code here to prepend to all .scss/.sass files. return ( - '@import "_colors"; ' + - content + '@import "colors"; ' + content ); }, }, From 072e9a043cdd4d844c7c4b201c4af6d0012f0316 Mon Sep 17 00:00:00 2001 From: Nicolas GEHIN Date: Tue, 7 Mar 2023 09:02:31 +0100 Subject: [PATCH 015/466] Remove the double conditions "options" + "variables" to obtain the discount line with or without taxes --- plugins/woocommerce/includes/abstracts/abstract-wc-order.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php index ae3bbb8a5f1..d2109701af5 100644 --- a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php +++ b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php @@ -2169,7 +2169,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { */ public function get_discount_to_display( $tax_display = '' ) { $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); - return apply_filters( 'woocommerce_order_discount_to_display', wc_price( $this->get_total_discount( 'excl' === $tax_display && 'excl' === get_option( 'woocommerce_tax_display_cart' ) ), array( 'currency' => $this->get_currency() ) ), $this ); + return apply_filters( 'woocommerce_order_discount_to_display', wc_price( $this->get_total_discount( 'excl' === $tax_display ), array( 'currency' => $this->get_currency() ) ), $this ); } /** From cf3c0c4c98126dfb9c08bd5cb741473cee3e8fad Mon Sep 17 00:00:00 2001 From: Nicolas GEHIN Date: Tue, 7 Mar 2023 10:22:52 +0100 Subject: [PATCH 016/466] Test unit for PR #36885 --- .../class-wc-abstract-order-test.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php b/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php index 5a8cdf5e67b..3b2cc7a9c46 100644 --- a/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php +++ b/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php @@ -269,4 +269,44 @@ class WC_Abstract_Order_Test extends WC_Unit_Test_Case { $this->assertEquals( $coupon->get_id(), $coupon_data['id'] ); $this->assertEquals( $coupon_code, $coupon_data['code'] ); } + + /** + * Test for get_discount_to_display which must return a value + * with and without tax whatever the setting of the options + * + * Issue :https://github.com/woocommerce/woocommerce/issues/36794 + */ + public function test_get_discount_to_display() { + // Set dummy data + $tax_rate = array( + 'tax_rate_country' => '', + 'tax_rate_state' => '', + 'tax_rate' => '20.0000', + 'tax_rate_name' => 'tax', + 'tax_rate_priority' => '1', + 'tax_rate_order' => '1', + ); + WC_Tax::_insert_tax_rate( $tax_rate ); + + $coupon = WC_Helper_Coupon::create_coupon(); + $product = WC_Helper_Product::create_simple_product(); + + $order = new WC_Order(); + $order->add_product( $product ); + $order->apply_coupon( $coupon ); + $order->calculate_totals(); + $order->save(); + + // Test + update_option( 'woocommerce_currency', 'USD' ); + update_option( 'woocommerce_currency_pos', 'right' ); + update_option( 'woocommerce_price_thousand_sep', '' ); + update_option( 'woocommerce_price_decimal_sep', '.' ); + update_option( 'woocommerce_price_num_decimals', 2 ); + + update_option( 'woocommerce_tax_display_cart', 'incl' ); + $this->assertEquals( '1.00$', strip_tags( $order->get_discount_to_display( 'excl' ) ) ); + $this->assertEquals( '1.20$', strip_tags( $order->get_discount_to_display( 'incl' ) ) ); + } + } From bd0da16eab22ff190fd5e5f38f8d7e2117f517b8 Mon Sep 17 00:00:00 2001 From: Michael Pretty Date: Fri, 17 Mar 2023 12:15:53 -0400 Subject: [PATCH 017/466] Replace information_schema queries in wc-update-functions.php with create table searches Partially fixes #37276 --- .../includes/wc-update-functions.php | 52 +++++++------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/plugins/woocommerce/includes/wc-update-functions.php b/plugins/woocommerce/includes/wc-update-functions.php index 0fe137d07a1..681dbb52e49 100644 --- a/plugins/woocommerce/includes/wc-update-functions.php +++ b/plugins/woocommerce/includes/wc-update-functions.php @@ -1843,18 +1843,14 @@ function wc_update_340_db_version() { function wc_update_343_cleanup_foreign_keys() { global $wpdb; - $results = $wpdb->get_results( - "SELECT CONSTRAINT_NAME - FROM information_schema.TABLE_CONSTRAINTS - WHERE CONSTRAINT_SCHEMA = '{$wpdb->dbname}' - AND CONSTRAINT_NAME LIKE '%wc_download_log_ib%' - AND CONSTRAINT_TYPE = 'FOREIGN KEY' - AND TABLE_NAME = '{$wpdb->prefix}wc_download_log'" - ); + $create_table_sql = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); - if ( $results ) { - foreach ( $results as $fk ) { - $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY {$fk->CONSTRAINT_NAME}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + if ( ! empty( $create_table_sql ) ) { + // Extract and remove the foreign key constraints matching %wc_download_log_ib% + if ( preg_match_all( '/CONSTRAINT `([^`]*wc_download_log_ib[^`]*)` FOREIGN KEY/', $create_table_sql, $matches ) && ! empty( $matches[1] ) ) { + foreach ( $matches[1] as $foreign_key_name ) { + $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY `{$foreign_key_name}`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + } } } } @@ -1912,18 +1908,13 @@ function wc_update_350_db_version() { */ function wc_update_352_drop_download_log_fk() { global $wpdb; - $results = $wpdb->get_results( - "SELECT CONSTRAINT_NAME - FROM information_schema.TABLE_CONSTRAINTS - WHERE CONSTRAINT_SCHEMA = '{$wpdb->dbname}' - AND CONSTRAINT_NAME = 'fk_wc_download_log_permission_id' - AND CONSTRAINT_TYPE = 'FOREIGN KEY' - AND TABLE_NAME = '{$wpdb->prefix}wc_download_log'" - ); - // We only need to drop the old key as WC_Install::create_tables() takes care of creating the new FK. - if ( $results ) { - $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY fk_wc_download_log_permission_id" ); // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + $create_table_sql = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); + + if ( ! empty( $create_table_sql ) ) { + if ( strpos( $create_table_sql, 'CONSTRAINT `fk_wc_download_log_permission_id` FOREIGN KEY' ) !== false ) { + $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY fk_wc_download_log_permission_id" ); // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared + } } } @@ -2451,17 +2442,14 @@ function wc_update_670_purge_comments_count_cache() { function wc_update_700_remove_download_log_fk() { global $wpdb; - $results = $wpdb->get_results( - "SELECT CONSTRAINT_NAME - FROM information_schema.TABLE_CONSTRAINTS - WHERE CONSTRAINT_SCHEMA = '{$wpdb->dbname}' - AND CONSTRAINT_TYPE = 'FOREIGN KEY' - AND TABLE_NAME = '{$wpdb->prefix}wc_download_log'" - ); + $create_table_sql = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); - if ( $results ) { - foreach ( $results as $fk ) { - $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY {$fk->CONSTRAINT_NAME}" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + if ( ! empty( $create_table_sql ) ) { + // Extract the foreign key constraints + if ( preg_match_all( '/CONSTRAINT `([^`]*)` FOREIGN KEY/', $create_table_sql, $matches ) && ! empty( $matches[1] ) ) { + foreach ( $matches[1] as $foreign_key_name ) { + $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY `{$foreign_key_name}`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + } } } } From 00bf0760be167ee724664bc1e6122a477d568228 Mon Sep 17 00:00:00 2001 From: Michael Pretty Date: Fri, 17 Mar 2023 12:29:50 -0400 Subject: [PATCH 018/466] Update changelog --- .../changelog/fix-37276-remove-information-schema-queries | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/fix-37276-remove-information-schema-queries diff --git a/plugins/woocommerce/changelog/fix-37276-remove-information-schema-queries b/plugins/woocommerce/changelog/fix-37276-remove-information-schema-queries new file mode 100644 index 00000000000..eb03b735fd3 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-37276-remove-information-schema-queries @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Replace information_schema queries in favor of create table parsing to remove foreign key constraints during updates From 2e3057052f41c386d58aa5ffbd0a35a95016b324 Mon Sep 17 00:00:00 2001 From: Michael Pretty Date: Fri, 17 Mar 2023 15:27:28 -0400 Subject: [PATCH 019/466] fix lint issues --- plugins/woocommerce/includes/wc-update-functions.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/woocommerce/includes/wc-update-functions.php b/plugins/woocommerce/includes/wc-update-functions.php index 681dbb52e49..7c9020db0c7 100644 --- a/plugins/woocommerce/includes/wc-update-functions.php +++ b/plugins/woocommerce/includes/wc-update-functions.php @@ -1846,7 +1846,7 @@ function wc_update_343_cleanup_foreign_keys() { $create_table_sql = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); if ( ! empty( $create_table_sql ) ) { - // Extract and remove the foreign key constraints matching %wc_download_log_ib% + // Extract and remove the foreign key constraints matching %wc_download_log_ib%. if ( preg_match_all( '/CONSTRAINT `([^`]*wc_download_log_ib[^`]*)` FOREIGN KEY/', $create_table_sql, $matches ) && ! empty( $matches[1] ) ) { foreach ( $matches[1] as $foreign_key_name ) { $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY `{$foreign_key_name}`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared @@ -2445,7 +2445,6 @@ function wc_update_700_remove_download_log_fk() { $create_table_sql = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); if ( ! empty( $create_table_sql ) ) { - // Extract the foreign key constraints if ( preg_match_all( '/CONSTRAINT `([^`]*)` FOREIGN KEY/', $create_table_sql, $matches ) && ! empty( $matches[1] ) ) { foreach ( $matches[1] as $foreign_key_name ) { $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_download_log DROP FOREIGN KEY `{$foreign_key_name}`" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared From 6c9cfaa7935290ff17c39db38cba7a716cb03744 Mon Sep 17 00:00:00 2001 From: Michael Pretty Date: Tue, 21 Mar 2023 10:46:49 -0400 Subject: [PATCH 020/466] Add unit tests for update-functions. --- .../php/includes/wc-update-functions-test.php | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 plugins/woocommerce/tests/php/includes/wc-update-functions-test.php diff --git a/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php b/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php new file mode 100644 index 00000000000..923f59478bc --- /dev/null +++ b/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php @@ -0,0 +1,84 @@ +query( + "ALTER TABLE `{$wpdb->prefix}wc_download_log` + ADD CONSTRAINT `wc_download_log_ib` + FOREIGN KEY (`permission_id`) + REFERENCES `{$wpdb->prefix}woocommerce_downloadable_product_permissions` (`permission_id`) ON DELETE CASCADE, + ADD CONSTRAINT `wc_download_log_ib_2` + FOREIGN KEY (`permission_id`) + REFERENCES `{$wpdb->prefix}woocommerce_downloadable_product_permissions` (`permission_id`) ON DELETE CASCADE" + ); + $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); + $this->assertNotFalse( strpos( $table_definition, "wc_download_log_ib" ) ); + $this->assertNotFalse( strpos( $table_definition, "wc_download_log_ib_2" ) ); + + include_once WC_ABSPATH . 'includes/wc-update-functions.php'; + + wc_update_343_cleanup_foreign_keys(); + + // Verify that the keys were properly removed + $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); + $this->assertFalse( strpos( $table_definition, "wc_download_log_ib" ) ); + } + + public function test_verify_wc_update_352_drop_download_log_fk_removes_foreign_keys() { + global $wpdb; + + // Add the foreign key between wc_download_log and wc_download_log_permission_id as it previously existed. + $wpdb->query( + "ALTER TABLE `{$wpdb->prefix}wc_download_log` + ADD CONSTRAINT `fk_wc_download_log_permission_id` + FOREIGN KEY (`permission_id`) + REFERENCES `{$wpdb->prefix}woocommerce_downloadable_product_permissions` (`permission_id`) ON DELETE CASCADE" + ); + $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); + $this->assertNotFalse( strpos( $table_definition, "fk_wc_download_log_permission_id" ) ); + + include_once WC_ABSPATH . 'includes/wc-update-functions.php'; + + wc_update_352_drop_download_log_fk(); + + // Verify that the key was properly removed + $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); + $this->assertFalse( strpos( $table_definition, "fk_wc_download_log_permission_id" ) ); + } + + public function test_verify_wc_update_700_remove_download_log_fk_removes_foreign_keys() { + global $wpdb; + + // Add the foreign key between wc_download_log and wc_download_log_permission_id as it previously existed. + $wpdb->query( + "ALTER TABLE `{$wpdb->prefix}wc_download_log` + ADD CONSTRAINT `fk_{$wpdb->prefix}wc_download_log_permission_id` + FOREIGN KEY (`permission_id`) + REFERENCES `{$wpdb->prefix}woocommerce_downloadable_product_permissions` (`permission_id`) ON DELETE CASCADE" + ); + $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); + $this->assertNotFalse( strpos( $table_definition, "fk_{$wpdb->prefix}wc_download_log_permission_id" ) ); + + include_once WC_ABSPATH . 'includes/wc-update-functions.php'; + + wc_update_700_remove_download_log_fk(); + + // Verify that the key was properly removed. + $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); + $this->assertFalse( strpos( $table_definition, "fk_{$wpdb->prefix}wc_download_log_permission_id" ) ); + } + + +} From 2db2e720c9fdf58735048c069f4db88e8e8705e4 Mon Sep 17 00:00:00 2001 From: Michael Pretty Date: Tue, 21 Mar 2023 10:55:08 -0400 Subject: [PATCH 021/466] Correcting quoting style for non-dynamic strings in tests --- .../tests/php/includes/wc-update-functions-test.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php b/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php index 923f59478bc..2a88a07e441 100644 --- a/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php +++ b/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php @@ -24,8 +24,8 @@ class WC_Update_Functions_Test extends \WC_Unit_Test_Case { REFERENCES `{$wpdb->prefix}woocommerce_downloadable_product_permissions` (`permission_id`) ON DELETE CASCADE" ); $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); - $this->assertNotFalse( strpos( $table_definition, "wc_download_log_ib" ) ); - $this->assertNotFalse( strpos( $table_definition, "wc_download_log_ib_2" ) ); + $this->assertNotFalse( strpos( $table_definition, 'wc_download_log_ib' ) ); + $this->assertNotFalse( strpos( $table_definition, 'wc_download_log_ib_2' ) ); include_once WC_ABSPATH . 'includes/wc-update-functions.php'; @@ -33,7 +33,7 @@ class WC_Update_Functions_Test extends \WC_Unit_Test_Case { // Verify that the keys were properly removed $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); - $this->assertFalse( strpos( $table_definition, "wc_download_log_ib" ) ); + $this->assertFalse( strpos( $table_definition, 'wc_download_log_ib' ) ); } public function test_verify_wc_update_352_drop_download_log_fk_removes_foreign_keys() { @@ -47,7 +47,7 @@ class WC_Update_Functions_Test extends \WC_Unit_Test_Case { REFERENCES `{$wpdb->prefix}woocommerce_downloadable_product_permissions` (`permission_id`) ON DELETE CASCADE" ); $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); - $this->assertNotFalse( strpos( $table_definition, "fk_wc_download_log_permission_id" ) ); + $this->assertNotFalse( strpos( $table_definition, 'fk_wc_download_log_permission_id' ) ); include_once WC_ABSPATH . 'includes/wc-update-functions.php'; @@ -55,7 +55,7 @@ class WC_Update_Functions_Test extends \WC_Unit_Test_Case { // Verify that the key was properly removed $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); - $this->assertFalse( strpos( $table_definition, "fk_wc_download_log_permission_id" ) ); + $this->assertFalse( strpos( $table_definition, 'fk_wc_download_log_permission_id' ) ); } public function test_verify_wc_update_700_remove_download_log_fk_removes_foreign_keys() { From d9e049d074dca03fa7f51d1bf208e902d161346b Mon Sep 17 00:00:00 2001 From: Michael Pretty Date: Tue, 21 Mar 2023 11:05:39 -0400 Subject: [PATCH 022/466] adding missing docblocks for tests --- .../tests/php/includes/wc-update-functions-test.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php b/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php index 2a88a07e441..3d07f42f915 100644 --- a/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php +++ b/plugins/woocommerce/tests/php/includes/wc-update-functions-test.php @@ -10,6 +10,9 @@ */ class WC_Update_Functions_Test extends \WC_Unit_Test_Case { + /** + * Test wc_update_343_cleanup_foreign_keys() function. + */ public function test_verify_wc_update_343_cleanup_foreign_keys_removes_foreign_keys() { global $wpdb; @@ -31,11 +34,14 @@ class WC_Update_Functions_Test extends \WC_Unit_Test_Case { wc_update_343_cleanup_foreign_keys(); - // Verify that the keys were properly removed + // Verify that the keys were properly removed. $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); $this->assertFalse( strpos( $table_definition, 'wc_download_log_ib' ) ); } + /** + * Test wc_update_352_drop_download_log_fk() function. + */ public function test_verify_wc_update_352_drop_download_log_fk_removes_foreign_keys() { global $wpdb; @@ -53,11 +59,14 @@ class WC_Update_Functions_Test extends \WC_Unit_Test_Case { wc_update_352_drop_download_log_fk(); - // Verify that the key was properly removed + // Verify that the key was properly removed. $table_definition = $wpdb->get_var( "SHOW CREATE TABLE {$wpdb->prefix}wc_download_log", 1 ); $this->assertFalse( strpos( $table_definition, 'fk_wc_download_log_permission_id' ) ); } + /** + * Test wc_update_700_remove_download_log_fk() function. + */ public function test_verify_wc_update_700_remove_download_log_fk_removes_foreign_keys() { global $wpdb; From f2cd586b2288f687a038d179513fe206dcde9e9d Mon Sep 17 00:00:00 2001 From: Anastasio <73900974+anastas10s-afk@users.noreply.github.com> Date: Tue, 21 Mar 2023 22:40:03 +0200 Subject: [PATCH 023/466] Updated WebP section in wc-core-functions.php Removed, previously added, unnecessary filter, and already existing function in wc-rest-functions.php. --- .../woocommerce/includes/wc-core-functions.php | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/plugins/woocommerce/includes/wc-core-functions.php b/plugins/woocommerce/includes/wc-core-functions.php index 06111cc9813..f615b5521e5 100644 --- a/plugins/woocommerce/includes/wc-core-functions.php +++ b/plugins/woocommerce/includes/wc-core-functions.php @@ -2589,24 +2589,8 @@ function wc_cache_get_multiple( $keys, $group = '', $force = false ) { * * WebP files should be able to be imported in WooCommerce, as they've been supported in WordPress core since v5.8. */ -add_filter( 'woocommerce_rest_allowed_image_mime_types', 'more_mimes_to_exts' ); function more_mimes_to_exts( $mime_to_ext ){ $mime_to_ext['webp'] = 'image/webp'; return $mime_to_ext; } - -function wc_rest_allowed_image_mime_types() { - return apply_filters( - 'woocommerce_rest_allowed_image_mime_types', - array( - 'jpg|jpeg|jpe' => 'image/jpeg', - 'gif' => 'image/gif', - 'png' => 'image/png', - 'bmp' => 'image/bmp', - 'tiff|tif' => 'image/tiff', - 'ico' => 'image/x-icon', - 'webp' => 'image/webp', - ) - ); -} From bd2de5ff60a6b97c5afd1d36c34346b795d528cc Mon Sep 17 00:00:00 2001 From: Anastasio <73900974+anastas10s-afk@users.noreply.github.com> Date: Tue, 21 Mar 2023 22:44:14 +0200 Subject: [PATCH 024/466] Added WebP support, at allowed_image_mime_types --- plugins/woocommerce/includes/wc-rest-functions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/woocommerce/includes/wc-rest-functions.php b/plugins/woocommerce/includes/wc-rest-functions.php index 9eed8f4ed49..39f0b15d92b 100644 --- a/plugins/woocommerce/includes/wc-rest-functions.php +++ b/plugins/woocommerce/includes/wc-rest-functions.php @@ -54,6 +54,7 @@ function wc_rest_allowed_image_mime_types() { 'bmp' => 'image/bmp', 'tiff|tif' => 'image/tiff', 'ico' => 'image/x-icon', + 'webp' => 'image/webp', ) ); } From 4f7705f6913fb8180f1956ae6766acc976748e38 Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Thu, 23 Mar 2023 23:19:34 +0800 Subject: [PATCH 025/466] Always use MarketingOverviewMultichannel for marketing page. --- plugins/woocommerce-admin/client/layout/controller.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/plugins/woocommerce-admin/client/layout/controller.js b/plugins/woocommerce-admin/client/layout/controller.js index 023b9af24c5..1863c17522e 100644 --- a/plugins/woocommerce-admin/client/layout/controller.js +++ b/plugins/woocommerce-admin/client/layout/controller.js @@ -51,11 +51,6 @@ const Dashboard = lazy( () => const Homescreen = lazy( () => import( /* webpackChunkName: "homescreen" */ '../homescreen' ) ); -const MarketingOverview = lazy( () => - import( - /* webpackChunkName: "marketing-overview" */ '../marketing/overview' - ) -); const MarketingOverviewMultichannel = lazy( () => import( /* webpackChunkName: "multichannel-marketing" */ '../marketing/overview-multichannel' @@ -157,9 +152,7 @@ export const getPages = () => { if ( window.wcAdminFeatures.marketing ) { pages.push( { - container: window.wcAdminFeatures[ 'multichannel-marketing' ] - ? MarketingOverviewMultichannel - : MarketingOverview, + container: MarketingOverviewMultichannel, path: '/marketing', breadcrumbs: [ ...initialBreadcrumbs, From 51a57c2808f01356ea7117ebb9ef0472bb60d692 Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Thu, 23 Mar 2023 23:28:55 +0800 Subject: [PATCH 026/466] Remove unused Button component in marketing directory. --- .../marketing/components/button/README.md | 22 ------------------- .../marketing/components/button/index.js | 22 ------------------- .../marketing/components/button/style.scss | 6 ----- .../client/marketing/components/index.js | 1 - 4 files changed, 51 deletions(-) delete mode 100644 plugins/woocommerce-admin/client/marketing/components/button/README.md delete mode 100644 plugins/woocommerce-admin/client/marketing/components/button/index.js delete mode 100644 plugins/woocommerce-admin/client/marketing/components/button/style.scss diff --git a/plugins/woocommerce-admin/client/marketing/components/button/README.md b/plugins/woocommerce-admin/client/marketing/components/button/README.md deleted file mode 100644 index 5c0298a9428..00000000000 --- a/plugins/woocommerce-admin/client/marketing/components/button/README.md +++ /dev/null @@ -1,22 +0,0 @@ -Button -=== - -This component creates simple reusable html `` element. - -## Usage - -```jsx - -``` - -### Props - -Name | Type | Default | Description ---- | --- | --- | --- -`className` | String | `null` | Additional class name to style the component diff --git a/plugins/woocommerce-admin/client/marketing/components/button/index.js b/plugins/woocommerce-admin/client/marketing/components/button/index.js deleted file mode 100644 index 0542f4fc998..00000000000 --- a/plugins/woocommerce-admin/client/marketing/components/button/index.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * External dependencies - */ -import { Button } from '@wordpress/components'; -import classnames from 'classnames'; - -/** - * Internal dependencies - */ -import './style.scss'; - -export default ( props ) => { - return ( - - ); - } - - getFinishSetupButton() { - return ( - - ); - } - - render() { - const { name, description, status, slug } = this.props; - let actions = null; - - switch ( status ) { - case 'installed': - actions = this.getActivateButton(); - break; - case 'activated': - actions = this.getFinishSetupButton(); - break; - case 'configured': - actions = this.getLinks(); - break; - } - - return ( -
- -
-
-

{ name }

- { status === 'configured' || ( -

- { description } -

- ) } -
-
- { actions } -
-
-
- ); - } -} - -InstalledExtensionRow.defaultProps = { - isLoading: false, -}; - -InstalledExtensionRow.propTypes = { - name: PropTypes.string.isRequired, - slug: PropTypes.string.isRequired, - description: PropTypes.string.isRequired, - status: PropTypes.string.isRequired, - settingsUrl: PropTypes.string, - docsUrl: PropTypes.string, - supportUrl: PropTypes.string, - dashboardUrl: PropTypes.string, - activatePlugin: PropTypes.func.isRequired, -}; - -export default InstalledExtensionRow; diff --git a/plugins/woocommerce-admin/client/marketing/overview/installed-extensions/style.scss b/plugins/woocommerce-admin/client/marketing/overview/installed-extensions/style.scss deleted file mode 100644 index 8968f5315fe..00000000000 --- a/plugins/woocommerce-admin/client/marketing/overview/installed-extensions/style.scss +++ /dev/null @@ -1,91 +0,0 @@ -.woocommerce-marketing-installed-extensions-card { - &__item { - display: flex; - align-items: center; - padding: 18px 24px; - - h4 { - font-weight: 400; - font-size: 16px; - margin: 0 0 5px; - color: $gray-900; - } - - p { - color: $gray-700; - margin: 0; - } - } - - &__item:not(:last-child) { - border-bottom: 1px solid $gray-200; - } - - &__item-text-and-actions { - display: flex; - flex-wrap: wrap; - align-items: center; - flex-grow: 2; - min-width: 0; // Flexbox truncated text fix - - @include breakpoint( '>600px' ) { - flex-wrap: nowrap; - } - } - - &__item-actions { - @include breakpoint( '>600px' ) { - text-align: right; - white-space: nowrap; - padding-left: 25px; - } - } - - &__item-description { - @include breakpoint( '>960px' ) { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 550px; - } - } - - &__item-links { - margin: 0; - padding: 0; - - li { - display: inline-block; - margin: 0 25px 0 0; - - @include breakpoint( '>600px' ) { - margin: 0 0 0 30px; - } - } - - a { - font-weight: 600; - color: var(--wp-admin-theme-color) !important; - text-decoration: none; - font-size: 14px; - } - } - - .woocommerce-admin-marketing-product-icon { - align-self: flex-start; - margin-right: 14px; - margin-top: 2px; // Align top of image with text - } - - &__item-text { - min-width: 0; // Flexbox truncated text fix - flex-grow: 2; - margin: 0 0 10px; - width: 100%; - - @include breakpoint( '>600px' ) { - margin: 0; - width: auto; - } - } -} diff --git a/plugins/woocommerce-admin/client/marketing/overview/section-slot/index.ts b/plugins/woocommerce-admin/client/marketing/overview/section-slot/index.ts deleted file mode 100644 index 38d712f793e..00000000000 --- a/plugins/woocommerce-admin/client/marketing/overview/section-slot/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './section-slot'; -export * from './utils'; diff --git a/plugins/woocommerce-admin/client/marketing/overview/section-slot/section-slot.tsx b/plugins/woocommerce-admin/client/marketing/overview/section-slot/section-slot.tsx deleted file mode 100644 index f06fc18292e..00000000000 --- a/plugins/woocommerce-admin/client/marketing/overview/section-slot/section-slot.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * External dependencies - */ -import { useSlot } from '@woocommerce/experimental'; -import classnames from 'classnames'; - -/** - * Internal dependencies - */ -import { - EXPERIMENTAL_WC_MARKETING_OVERVIEW_SECTION_SLOT_NAME, - WooMarketingOverviewSection, -} from './utils'; - -export const MarketingOverviewSectionSlot = ( { - className, -}: { - className: string; -} ) => { - const slot = useSlot( - EXPERIMENTAL_WC_MARKETING_OVERVIEW_SECTION_SLOT_NAME - ); - const hasFills = Boolean( slot?.fills?.length ); - - if ( ! hasFills ) { - return null; - } - return ( -
- -
- ); -}; diff --git a/plugins/woocommerce-admin/client/marketing/overview/section-slot/utils.tsx b/plugins/woocommerce-admin/client/marketing/overview/section-slot/utils.tsx deleted file mode 100644 index 5c9a9de9ea0..00000000000 --- a/plugins/woocommerce-admin/client/marketing/overview/section-slot/utils.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/** - * External dependencies - */ -import { Slot, Fill } from '@wordpress/components'; -import { - createOrderedChildren, - sortFillsByOrder, -} from '@woocommerce/components'; - -export const EXPERIMENTAL_WC_MARKETING_OVERVIEW_SECTION_SLOT_NAME = - 'experimental_woocommerce_marketing_overview_section'; -/** - * Create a Fill for extensions to add a section to the Marketing Overview page. - * - * @slotFill WooMarketingOverviewSection - * @scope woocommerce-admin - * @example - * const MySection = () => ( - * - *
- *
- * Slotfill goes in here! - *
- *
- *
- * ); - * - * registerPlugin( 'my-extension', { - * render: MySection, - * scope: 'woocommerce-admin', - * } ); - * @param {Object} param0 - * @param {Array} param0.children - Node children. - * @param {Array} param0.order - Node order. - */ -export const WooMarketingOverviewSection = ( { - children, - order = 1, -}: { - children: React.ReactNode; - order?: number; -} ) => { - return ( - - { ( fillProps: Fill.Props ) => { - return createOrderedChildren( children, order, fillProps ); - } } - - ); -}; - -WooMarketingOverviewSection.Slot = ( { - fillProps, -}: { - fillProps?: Slot.Props; -} ) => ( - - { sortFillsByOrder } - -); diff --git a/plugins/woocommerce-admin/client/marketing/overview/style.scss b/plugins/woocommerce-admin/client/marketing/overview/style.scss deleted file mode 100644 index eaf6fdbbf4f..00000000000 --- a/plugins/woocommerce-admin/client/marketing/overview/style.scss +++ /dev/null @@ -1,8 +0,0 @@ -.woocommerce-marketing-overview { - max-width: 1032px; - margin: 0 auto; - - .components-card { - margin-bottom: 24px; - } -} diff --git a/plugins/woocommerce-admin/client/marketing/overview/welcome-card/images/welcome.svg b/plugins/woocommerce-admin/client/marketing/overview/welcome-card/images/welcome.svg deleted file mode 100644 index ae8d8a7a75f..00000000000 --- a/plugins/woocommerce-admin/client/marketing/overview/welcome-card/images/welcome.svg +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/woocommerce-admin/client/marketing/overview/welcome-card/index.js b/plugins/woocommerce-admin/client/marketing/overview/welcome-card/index.js deleted file mode 100644 index 99ed62c0b46..00000000000 --- a/plugins/woocommerce-admin/client/marketing/overview/welcome-card/index.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * External dependencies - */ -import { __ } from '@wordpress/i18n'; -import { Button, Card, CardBody } from '@wordpress/components'; -import CrossIcon from 'gridicons/dist/cross'; -import { compose } from '@wordpress/compose'; -import { withDispatch, withSelect } from '@wordpress/data'; -import PropTypes from 'prop-types'; -import { OPTIONS_STORE_NAME } from '@woocommerce/data'; -import { recordEvent } from '@woocommerce/tracks'; - -/** - * Internal dependencies - */ -import './style.scss'; -import WelcomeImage from './images/welcome.svg'; - -const WelcomeCard = ( { isHidden, updateOptions } ) => { - const hide = () => { - updateOptions( { - woocommerce_marketing_overview_welcome_hidden: 'yes', - } ); - recordEvent( 'marketing_intro_close', {} ); - }; - - if ( isHidden ) { - return null; - } - - return ( - - - - -

- { __( - 'Grow your customer base and increase your sales with marketing tools built for WooCommerce', - 'woocommerce' - ) } -

-
-
- ); -}; - -WelcomeCard.propTypes = { - /** - * Whether the card is hidden. - */ - isHidden: PropTypes.bool.isRequired, - /** - * updateOptions function. - */ - updateOptions: PropTypes.func.isRequired, -}; - -// named export -export { WelcomeCard }; - -// default export -export default compose( - withSelect( ( select ) => { - const { getOption, isOptionsUpdating } = select( OPTIONS_STORE_NAME ); - const isUpdateRequesting = isOptionsUpdating(); - - return { - isHidden: - getOption( 'woocommerce_marketing_overview_welcome_hidden' ) === - 'yes' || isUpdateRequesting, - }; - } ), - withDispatch( ( dispatch ) => { - const { updateOptions } = dispatch( OPTIONS_STORE_NAME ); - return { - updateOptions, - }; - } ) -)( WelcomeCard ); diff --git a/plugins/woocommerce-admin/client/marketing/overview/welcome-card/style.scss b/plugins/woocommerce-admin/client/marketing/overview/welcome-card/style.scss deleted file mode 100644 index 3a6549a89ee..00000000000 --- a/plugins/woocommerce-admin/client/marketing/overview/welcome-card/style.scss +++ /dev/null @@ -1,76 +0,0 @@ -.woocommerce-marketing-overview-welcome-card { - position: relative; - - .components-card__body { - display: flex; - justify-content: center; - align-items: center; - flex-wrap: wrap; - padding: 22px; - - @include breakpoint( '>600px' ) { - flex-wrap: nowrap; - } - - @include breakpoint( '>960px' ) { - padding: 32px 108px; - } - } - - &__hide-button { - display: flex; - align-items: center; - padding: 8px; - margin: 0; - border: none; - background: none; - color: $gray-700; - overflow: hidden; - border-radius: 4px; - position: absolute; - top: 10px; - right: 10px; - - // Ensure that even SVG icons that don't include the .dashicon class are colored. - svg { - fill: currentColor; - outline: none; - } - - &:not(:disabled):not([aria-disabled='true']):not(.is-default):hover { - @include button-style__hover; - } - - &:not(:disabled):not([aria-disabled='true']):not(.is-default):active { - @include button-style__active; - } - - &[aria-disabled='true']:focus, - &:disabled:focus { - box-shadow: none; - } - } - - h3 { - font-size: 20px; - line-height: 26px; - font-weight: normal; - text-align: center; - margin: 1em 0 0; - - @include breakpoint( '>600px' ) { - text-align: left; - margin: 0 0 0 20px; - } - - @include breakpoint( '>960px' ) { - font-size: 24px; - line-height: 32px; - } - } - - img { - width: 231px; - flex: none; - } -} diff --git a/plugins/woocommerce-admin/client/marketing/overview/welcome-card/test/index.js b/plugins/woocommerce-admin/client/marketing/overview/welcome-card/test/index.js deleted file mode 100644 index b737ff6f204..00000000000 --- a/plugins/woocommerce-admin/client/marketing/overview/welcome-card/test/index.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * External dependencies - */ -import { recordEvent } from '@woocommerce/tracks'; -import { render, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { createElement } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { WelcomeCard } from '../index.js'; - -jest.mock( '@woocommerce/tracks' ); -jest.mock( '@woocommerce/settings' ); - -describe( 'WelcomeCard hide button', () => { - it( 'should record an event when clicked', () => { - const { getByRole } = render( - - ); - - userEvent.click( getByRole( 'button', { name: 'Hide' } ) ); - - expect( recordEvent ).toHaveBeenCalledTimes( 1 ); - expect( recordEvent ).toHaveBeenCalledWith( - 'marketing_intro_close', - {} - ); - } ); - - it( 'should update option when clicked', async () => { - const mockUpdateOptions = jest.fn(); - const { getByRole } = render( - - ); - - userEvent.click( getByRole( 'button' ) ); - - await waitFor( () => - expect( mockUpdateOptions ).toHaveBeenCalledTimes( 1 ) - ); - expect( mockUpdateOptions ).toHaveBeenCalledWith( { - woocommerce_marketing_overview_welcome_hidden: 'yes', - } ); - } ); -} ); - -describe( 'Component visibility can be toggled', () => { - it( 'WelcomeCard should be visible if isHidden is false', () => { - const { getByRole } = render( - - ); - - expect( getByRole( 'button' ) ).toBeInTheDocument(); - } ); - - it( 'WelcomeCard should be hidden if isHidden is true', () => { - const { queryByRole } = render( - - ); - - expect( queryByRole( 'button' ) ).toBeNull(); - } ); -} ); From ec2cc54c095bdfef98b7720ce87be7e7b5aeaa77 Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Fri, 24 Mar 2023 20:01:44 +0800 Subject: [PATCH 029/466] Remove feature toggle in WC Settings page. --- .../woocommerce/client/admin/config/core.json | 1 - .../client/admin/config/development.json | 1 - .../src/Admin/Features/Features.php | 2 - .../Features/MultichannelMarketing/Init.php | 87 ------------------- 4 files changed, 91 deletions(-) delete mode 100644 plugins/woocommerce/src/Admin/Features/MultichannelMarketing/Init.php diff --git a/plugins/woocommerce/client/admin/config/core.json b/plugins/woocommerce/client/admin/config/core.json index 48e6fe51ff5..f796d84a040 100644 --- a/plugins/woocommerce/client/admin/config/core.json +++ b/plugins/woocommerce/client/admin/config/core.json @@ -11,7 +11,6 @@ "shipping-setting-tour": true, "homescreen": true, "marketing": true, - "multichannel-marketing": true, "minified-js": false, "mobile-app-banner": true, "navigation": true, diff --git a/plugins/woocommerce/client/admin/config/development.json b/plugins/woocommerce/client/admin/config/development.json index 39286384fb8..b2511893a64 100644 --- a/plugins/woocommerce/client/admin/config/development.json +++ b/plugins/woocommerce/client/admin/config/development.json @@ -11,7 +11,6 @@ "shipping-setting-tour": true, "homescreen": true, "marketing": true, - "multichannel-marketing": true, "minified-js": true, "mobile-app-banner": true, "navigation": true, diff --git a/plugins/woocommerce/src/Admin/Features/Features.php b/plugins/woocommerce/src/Admin/Features/Features.php index cca803dbca9..681b322bb6c 100644 --- a/plugins/woocommerce/src/Admin/Features/Features.php +++ b/plugins/woocommerce/src/Admin/Features/Features.php @@ -26,7 +26,6 @@ class Features { * @var array */ protected static $optional_features = array( - 'multichannel-marketing' => array( 'default' => 'no' ), 'navigation' => array( 'default' => 'no' ), 'settings' => array( 'default' => 'no' ), 'analytics' => array( 'default' => 'yes' ), @@ -39,7 +38,6 @@ class Features { * @var array */ protected static $beta_features = array( - 'multichannel-marketing', 'navigation', 'new-product-management-experience', 'block-editor-feature-enabled', diff --git a/plugins/woocommerce/src/Admin/Features/MultichannelMarketing/Init.php b/plugins/woocommerce/src/Admin/Features/MultichannelMarketing/Init.php deleted file mode 100644 index 1d9aa43c805..00000000000 --- a/plugins/woocommerce/src/Admin/Features/MultichannelMarketing/Init.php +++ /dev/null @@ -1,87 +0,0 @@ - __( 'Marketing', 'woocommerce' ), - 'desc' => $description, - 'id' => self::TOGGLE_OPTION_NAME, - 'type' => 'checkbox', - 'class' => '', - ); - - return $features; - } - - /** - * Reloads the page when the option is toggled to make sure all Multichannel Marketing features are loaded. - * - * @param string $old_value Old value. - * @param string $value New value. - */ - public static function reload_page_on_toggle( $old_value, $value ) { - if ( $old_value === $value ) { - return; - } - - self::$is_updated = true; - } - - /** - * Reload the page if the setting has been updated. - */ - public static function maybe_reload_page() { - if ( ! isset( $_SERVER['REQUEST_URI'] ) || ! self::$is_updated ) { - return; - } - - wp_safe_redirect( wp_unslash( $_SERVER['REQUEST_URI'] ) ); - exit(); - } -} From e1226eb9c37c478f62a4dbd56f287276dab8f9c4 Mon Sep 17 00:00:00 2001 From: John Greys Date: Fri, 24 Mar 2023 14:20:25 +0000 Subject: [PATCH 030/466] Dev - Add customer object parameter to taxable address filter --- plugins/woocommerce/includes/class-wc-customer.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/woocommerce/includes/class-wc-customer.php b/plugins/woocommerce/includes/class-wc-customer.php index 43587c5d1e3..e967ce3b3e0 100644 --- a/plugins/woocommerce/includes/class-wc-customer.php +++ b/plugins/woocommerce/includes/class-wc-customer.php @@ -207,7 +207,15 @@ class WC_Customer extends WC_Legacy_Customer { $city = $this->get_shipping_city(); } - return apply_filters( 'woocommerce_customer_taxable_address', array( $country, $state, $postcode, $city ) ); + /** + * Filters the taxable address for a given customer. + * + * @param array $taxable_address An array of country, state, postcode, and city for the customer's taxable address. + * @param object $customer The customer object for which the taxable address is being requested. + * + * @return array The filtered taxable address for the customer. + */ + return apply_filters( 'woocommerce_customer_taxable_address', array( $country, $state, $postcode, $city ), $this ); } /** From c8d0ab3019d3b33379c821087829a1522efa94f5 Mon Sep 17 00:00:00 2001 From: John Greys Date: Fri, 24 Mar 2023 14:22:41 +0000 Subject: [PATCH 031/466] Add changelog file --- .../changelog/add-customer-object-taxable-address-filter | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/add-customer-object-taxable-address-filter diff --git a/plugins/woocommerce/changelog/add-customer-object-taxable-address-filter b/plugins/woocommerce/changelog/add-customer-object-taxable-address-filter new file mode 100644 index 00000000000..24dddd173b1 --- /dev/null +++ b/plugins/woocommerce/changelog/add-customer-object-taxable-address-filter @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Dev - Add customer object parameter to taxable address filter From 126187b79481783e3457a1173157fe94fa109e74 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Fri, 24 Mar 2023 21:20:53 +0530 Subject: [PATCH 032/466] Fix imported ContainerInterface (#37334) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Viktor Szépe --- plugins/woocommerce/changelog/pr-37055 | 4 ++++ plugins/woocommerce/src/Proxies/LegacyProxy.php | 2 +- plugins/woocommerce/tests/php/src/Proxies/LegacyProxyTest.php | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 plugins/woocommerce/changelog/pr-37055 diff --git a/plugins/woocommerce/changelog/pr-37055 b/plugins/woocommerce/changelog/pr-37055 new file mode 100644 index 00000000000..ce0f7867340 --- /dev/null +++ b/plugins/woocommerce/changelog/pr-37055 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Corrects imported ContainerInterface. It was not replaced because of the leading backslash. diff --git a/plugins/woocommerce/src/Proxies/LegacyProxy.php b/plugins/woocommerce/src/Proxies/LegacyProxy.php index e968bdd423e..1ab50dea99c 100644 --- a/plugins/woocommerce/src/Proxies/LegacyProxy.php +++ b/plugins/woocommerce/src/Proxies/LegacyProxy.php @@ -6,7 +6,7 @@ namespace Automattic\WooCommerce\Proxies; use Automattic\WooCommerce\Internal\DependencyManagement\Definition; -use Psr\Container\ContainerInterface; +use Automattic\WooCommerce\Vendor\Psr\Container\ContainerInterface; /** * Proxy class to access legacy WooCommerce functionality. diff --git a/plugins/woocommerce/tests/php/src/Proxies/LegacyProxyTest.php b/plugins/woocommerce/tests/php/src/Proxies/LegacyProxyTest.php index 05f427bd640..1b05d059e1a 100644 --- a/plugins/woocommerce/tests/php/src/Proxies/LegacyProxyTest.php +++ b/plugins/woocommerce/tests/php/src/Proxies/LegacyProxyTest.php @@ -32,7 +32,7 @@ class LegacyProxyTest extends \WC_Unit_Test_Case { */ public function test_get_instance_of_throws_when_trying_to_get_a_namespaced_class() { $this->expectException( \Exception::class ); - $this->expectExceptionMessage( 'The LegacyProxy class is not intended for getting instances of classes in the src directory, please use ' . Definition::INJECTION_METHOD . ' method injection or the instance of Psr\Container\ContainerInterface for that.' ); + $this->expectExceptionMessage( 'The LegacyProxy class is not intended for getting instances of classes in the src directory, please use ' . Definition::INJECTION_METHOD . ' method injection or the instance of Automattic\WooCommerce\Vendor\Psr\Container\ContainerInterface for that.' ); $this->sut->get_instance_of( DependencyClass::class ); } From 49299de1fd6ce41e143246c5733ca99b59ca4dbd Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Sat, 25 Mar 2023 00:40:29 +0800 Subject: [PATCH 033/466] Add changelog. --- .../feature-37367-marketing-multichannel-remove-toggle | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/feature-37367-marketing-multichannel-remove-toggle diff --git a/plugins/woocommerce/changelog/feature-37367-marketing-multichannel-remove-toggle b/plugins/woocommerce/changelog/feature-37367-marketing-multichannel-remove-toggle new file mode 100644 index 00000000000..54f79f3f168 --- /dev/null +++ b/plugins/woocommerce/changelog/feature-37367-marketing-multichannel-remove-toggle @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Make Multichannel Marketing the default new UI for Marketing page. From 6a6dd1a710af66d154e3d5328feac7034de33321 Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Sat, 25 Mar 2023 01:22:14 +0800 Subject: [PATCH 034/466] Fix failed api-core-tests. --- .../api-core-tests/tests/settings/settings-crud.test.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/plugins/woocommerce/tests/api-core-tests/tests/settings/settings-crud.test.js b/plugins/woocommerce/tests/api-core-tests/tests/settings/settings-crud.test.js index b8d10686e3c..f5314972d76 100644 --- a/plugins/woocommerce/tests/api-core-tests/tests/settings/settings-crud.test.js +++ b/plugins/woocommerce/tests/api-core-tests/tests/settings/settings-crud.test.js @@ -1693,15 +1693,6 @@ test.describe('Settings API tests: CRUD', () => { "value": "yes", }) ])); - expect(responseJSON).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - "id": "woocommerce_multichannel_marketing_enabled", - "label": "Marketing", - "description": "Enables the new WooCommerce Multichannel Marketing experience in the Marketing page", - "type": "checkbox", - }) - ])); expect(responseJSON).toEqual( expect.arrayContaining([ expect.objectContaining({ From 046b0195ab6ba23733dc0e28ab9be047d496ea61 Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Sat, 25 Mar 2023 01:32:14 +0800 Subject: [PATCH 035/466] Update e2e test for marketing page. --- .../tests/admin-marketing/overview.spec.js | 40 +------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/plugins/woocommerce/tests/e2e-pw/tests/admin-marketing/overview.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/admin-marketing/overview.spec.js index 5de02a455b0..42e19921939 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/admin-marketing/overview.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/admin-marketing/overview.spec.js @@ -3,47 +3,9 @@ const { test, expect } = require( '@playwright/test' ); test.describe( 'Marketing page', () => { test.use( { storageState: process.env.ADMINSTATE } ); - test( 'A user can disable the Multichannel Marketing feature in WC Settings and view the Marketing > Overview page without it crashing', async ( { + test( 'A user can view the Marketing > Overview page without it crashing', async ( { page, } ) => { - // Go to WC Settings > Advanced > Features page. - await page.goto( - 'wp-admin/admin.php?page=wc-settings&tab=advanced§ion=features' - ); - - // Disable multichannel marketing experience by unchecking the checkbox and clicking on "Save changes" button. - await page - .locator( - '"Enables the new WooCommerce Multichannel Marketing experience in the Marketing page"' - ) - .uncheck(); - await page.locator( '"Save changes"' ).click(); - - // Go to the Marketing page. - await page.goto( 'wp-admin/admin.php?page=wc-admin&path=%2Fmarketing' ); - - // Users should see the knowledge base card. - await expect( - page.locator( '"WooCommerce knowledge base"' ) - ).toBeVisible(); - } ); - - test( 'A user can enable the Multichannel Marketing feature in WC Settings and view the Marketing > Overview page without it crashing', async ( { - page, - } ) => { - // Go to WC Settings > Advanced > Features page. - await page.goto( - 'wp-admin/admin.php?page=wc-settings&tab=advanced§ion=features' - ); - - // Enable multichannel marketing experience by checking the checkbox and clicking on "Save changes" button. - await page - .locator( - '"Enables the new WooCommerce Multichannel Marketing experience in the Marketing page"' - ) - .check(); - await page.locator( '"Save changes"' ).click(); - // Go to the Marketing page. await page.goto( 'wp-admin/admin.php?page=wc-admin&path=%2Fmarketing' ); From 1f698d258384e1c4c525e63f73094c3fc0167979 Mon Sep 17 00:00:00 2001 From: Nathan Silveira Date: Fri, 24 Mar 2023 14:49:45 -0300 Subject: [PATCH 036/466] Create experimental SelectTree component (#37319) * Create a new experimental component TreeSelect Add functionality to TreeControl to support additional scenarios * Fix a bug where the shouldNotRecursivelySelect was not passed to Trees inside Tree Items * Do not collapse nodes inside useEffect if they are already expanded * Add storybook example for SelectTreeControl * Move component to its own directory * Remove wrong comment * Fix CSS import path * Remove getFilteredItems from select-tree * Stop using getFilteredItems inside select tree Create new shouldShowCreateButton prop to allow receiving only filtered items inside component Remove support to show custom getItemLabel and getItemValue * Change interface * Rename back Item type names * Rename back value * Fix lint error and remove passing ref to treeProps * Make id mandatory in SelectTree * Refactor * Improve selector to focus on create button --- .../js/components/changelog/add-tree-select | 4 + .../experimental-select-control/combo-box.tsx | 23 +- .../src/experimental-select-control/types.ts | 3 +- .../experimental-select-tree-control/index.ts | 1 + .../select-tree.scss | 15 ++ .../select-tree.tsx | 199 ++++++++++++++++++ .../stories/index.tsx | 101 +++++++++ .../hooks/use-expander.ts | 3 +- .../hooks/use-keyboard.ts | 7 +- .../hooks/use-selection.ts | 18 +- .../hooks/use-tree-item.ts | 9 + .../hooks/use-tree.ts | 7 +- .../src/experimental-tree-control/tree.scss | 9 + .../src/experimental-tree-control/tree.tsx | 104 +++++++-- .../src/experimental-tree-control/types.ts | 26 ++- packages/js/components/src/index.ts | 6 +- packages/js/components/src/style.scss | 1 + 17 files changed, 494 insertions(+), 42 deletions(-) create mode 100644 packages/js/components/changelog/add-tree-select create mode 100644 packages/js/components/src/experimental-select-tree-control/index.ts create mode 100644 packages/js/components/src/experimental-select-tree-control/select-tree.scss create mode 100644 packages/js/components/src/experimental-select-tree-control/select-tree.tsx create mode 100644 packages/js/components/src/experimental-select-tree-control/stories/index.tsx diff --git a/packages/js/components/changelog/add-tree-select b/packages/js/components/changelog/add-tree-select new file mode 100644 index 00000000000..6bf7d04c3de --- /dev/null +++ b/packages/js/components/changelog/add-tree-select @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Create SelectTree component that uses TreeControl diff --git a/packages/js/components/src/experimental-select-control/combo-box.tsx b/packages/js/components/src/experimental-select-control/combo-box.tsx index 1589c09f13a..91cca1c1ecf 100644 --- a/packages/js/components/src/experimental-select-control/combo-box.tsx +++ b/packages/js/components/src/experimental-select-control/combo-box.tsx @@ -4,15 +4,10 @@ import { createElement, MouseEvent, useRef } from 'react'; import classNames from 'classnames'; -/** - * Internal dependencies - */ -import { Props } from './types'; - type ComboBoxProps = { children?: JSX.Element | JSX.Element[] | null; - comboBoxProps: Props; - inputProps: Props; + comboBoxProps: JSX.IntrinsicElements[ 'div' ]; + inputProps: JSX.IntrinsicElements[ 'input' ]; suffix?: JSX.Element | null; }; @@ -60,12 +55,14 @@ export const ComboBox = ( { { - inputRef.current = node; - ( - inputProps.ref as unknown as ( - node: HTMLInputElement | null - ) => void - )( node ); + if ( typeof inputProps.ref === 'function' ) { + inputRef.current = node; + ( + inputProps.ref as unknown as ( + node: HTMLInputElement | null + ) => void + )( node ); + } } } /> diff --git a/packages/js/components/src/experimental-select-control/types.ts b/packages/js/components/src/experimental-select-control/types.ts index 7d1a85f8cee..331b2d148b8 100644 --- a/packages/js/components/src/experimental-select-control/types.ts +++ b/packages/js/components/src/experimental-select-control/types.ts @@ -16,7 +16,8 @@ export type DefaultItemType = { export type SelectedType< ItemType > = ItemType | null; export type Props = { - [ key: string ]: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [ key: string ]: any; }; export type getItemPropsType< ItemType > = ( diff --git a/packages/js/components/src/experimental-select-tree-control/index.ts b/packages/js/components/src/experimental-select-tree-control/index.ts new file mode 100644 index 00000000000..9ea352bc18b --- /dev/null +++ b/packages/js/components/src/experimental-select-tree-control/index.ts @@ -0,0 +1 @@ +export * from './select-tree'; diff --git a/packages/js/components/src/experimental-select-tree-control/select-tree.scss b/packages/js/components/src/experimental-select-tree-control/select-tree.scss new file mode 100644 index 00000000000..509dadf5d6c --- /dev/null +++ b/packages/js/components/src/experimental-select-tree-control/select-tree.scss @@ -0,0 +1,15 @@ +.woocommerce-experimental-select-control__combo-box-wrapper { + &:focus { + box-shadow: 0 0 0 1px var( --wp-admin-theme-color ); + border-color: var( --wp-admin-theme-color ); + } +} + +.woocommerce-experimental-select-tree-control__dropdown { + display: block; +} + +// That's the only way I could remove the padding from the @wordpress/components Dropdown. +.woocommerce-experimental-select-tree-control__dropdown-content .components-popover__content { + padding: 0; +} diff --git a/packages/js/components/src/experimental-select-tree-control/select-tree.tsx b/packages/js/components/src/experimental-select-tree-control/select-tree.tsx new file mode 100644 index 00000000000..7406ea9d0f7 --- /dev/null +++ b/packages/js/components/src/experimental-select-tree-control/select-tree.tsx @@ -0,0 +1,199 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/** + * External dependencies + */ +import { createElement, useRef, useState } from 'react'; +import classNames from 'classnames'; +import { search } from '@wordpress/icons'; +import { Dropdown, Spinner } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { useLinkedTree } from '../experimental-tree-control/hooks/use-linked-tree'; +import { Tree } from '../experimental-tree-control/tree'; +import { + Item, + LinkedTree, + TreeControlProps, +} from '../experimental-tree-control/types'; +import { SelectedItems } from '../experimental-select-control/selected-items'; +import { ComboBox } from '../experimental-select-control/combo-box'; +import { SuffixIcon } from '../experimental-select-control/suffix-icon'; + +interface SelectTreeProps extends TreeControlProps { + id: string; + selected?: Item[]; + getSelectedItemProps?: any; + treeRef?: React.ForwardedRef< HTMLOListElement >; + suffix?: JSX.Element | null; + isLoading?: boolean; + label: string | JSX.Element; + onInputChange?: ( value: string | undefined ) => void; +} + +export const SelectTree = function SelectTree( { + items, + getSelectedItemProps, + treeRef: ref, + suffix = , + placeholder, + isLoading, + onInputChange, + shouldShowCreateButton, + ...props +}: SelectTreeProps ) { + const linkedTree = useLinkedTree( items ); + + const [ isFocused, setIsFocused ] = useState( false ); + + const comboBoxRef = useRef< HTMLDivElement >( null ); + + // getting the parent's parent div width to set the width of the dropdown + const comboBoxWidth = + comboBoxRef.current?.parentElement?.parentElement?.getBoundingClientRect() + .width; + + const shouldItemBeExpanded = ( item: LinkedTree ): boolean => { + if ( ! props.createValue || ! item.children?.length ) return false; + return item.children.some( ( child ) => { + if ( + new RegExp( props.createValue || '', 'ig' ).test( + child.data.label + ) + ) { + return true; + } + return shouldItemBeExpanded( child ); + } ); + }; + + return ( + + isLoading ? ( +
+ +
+ ) : ( + + ) + } + renderToggle={ ( { isOpen, onToggle, onClose } ) => ( +
+ + { + if ( ! isOpen ) { + onToggle(); + } + setIsFocused( true ); + }, + onBlur: ( event ) => { + // if blurring to an element inside the dropdown, don't close it + if ( + isOpen && + ! document + .querySelector( + '.woocommerce-experimental-select-control ~ .components-popover' + ) + ?.contains( event.relatedTarget ) + ) { + onClose(); + } + setIsFocused( false ); + }, + onKeyDown: ( event ) => { + const baseQuery = + '.woocommerce-experimental-select-tree-control__dropdown > .components-popover'; + if ( event.key === 'ArrowDown' ) { + event.preventDefault(); + // focus on the first element from the Popover + ( + document.querySelector( + `${ baseQuery } input, ${ baseQuery } button` + ) as + | HTMLInputElement + | HTMLButtonElement + )?.focus(); + } + if ( event.key === 'Tab' ) { + onClose(); + } + }, + onChange: ( event ) => + onInputChange && + onInputChange( event.target.value ), + placeholder, + } } + suffix={ suffix } + > + item?.label || '' } + getItemValue={ ( item ) => item?.value || '' } + onRemove={ ( item ) => { + if ( + ! Array.isArray( item ) && + props.onRemove + ) { + props.onRemove( item ); + onClose(); + } + } } + getSelectedItemProps={ () => ( {} ) } + /> + +
+ ) } + /> + ); +}; diff --git a/packages/js/components/src/experimental-select-tree-control/stories/index.tsx b/packages/js/components/src/experimental-select-tree-control/stories/index.tsx new file mode 100644 index 00000000000..4034c571905 --- /dev/null +++ b/packages/js/components/src/experimental-select-tree-control/stories/index.tsx @@ -0,0 +1,101 @@ +/** + * External dependencies + */ + +import React, { createElement } from 'react'; + +/** + * Internal dependencies + */ +import { SelectTree } from '../select-tree'; +import { Item } from '../../experimental-tree-control/types'; + +const listItems: Item[] = [ + { value: '1', label: 'Technology' }, + { value: '1.1', label: 'Notebooks', parent: '1' }, + { value: '1.2', label: 'Phones', parent: '1' }, + { value: '1.2.1', label: 'iPhone', parent: '1.2' }, + { value: '1.2.1.1', label: 'iPhone 14 Pro', parent: '1.2.1' }, + { value: '1.2.1.2', label: 'iPhone 14 Pro Max', parent: '1.2.1' }, + { value: '1.2.2', label: 'Samsung', parent: '1.2' }, + { value: '1.2.2.1', label: 'Samsung Galaxy 22 Plus', parent: '1.2.2' }, + { value: '1.2.2.2', label: 'Samsung Galaxy 22 Ultra', parent: '1.2.2' }, + { value: '1.3', label: 'Wearables', parent: '1' }, + { value: '2', label: 'Hardware' }, + { value: '2.1', label: 'CPU', parent: '2' }, + { value: '2.2', label: 'GPU', parent: '2' }, + { value: '2.3', label: 'Memory RAM', parent: '2' }, + { value: '3', label: 'Other' }, +]; + +const filterItems = ( items: Item[], searchValue ) => { + const filteredItems = items.filter( ( e ) => + e.label.includes( searchValue ) + ); + const itemsToIterate = [ ...filteredItems ]; + while ( itemsToIterate.length > 0 ) { + // The filter should include the parents of the filtered items + const element = itemsToIterate.pop(); + if ( element ) { + const parent = listItems.find( + ( item ) => item.value === element.parent + ); + if ( parent && ! filteredItems.includes( parent ) ) { + filteredItems.push( parent ); + itemsToIterate.push( parent ); + } + } + } + return filteredItems; +}; + +export const MultipleSelectTree: React.FC = () => { + const [ value, setValue ] = React.useState( '' ); + const [ selected, setSelected ] = React.useState< Item[] >( [] ); + + const items = filterItems( listItems, value ); + + return ( + + ! value || + listItems.findIndex( ( item ) => item.label === typedValue ) === + -1 + } + createValue={ value } + // eslint-disable-next-line no-alert + onCreateNew={ () => alert( 'create new called' ) } + onInputChange={ ( a ) => setValue( a || '' ) } + onSelect={ ( selectedItems ) => { + if ( Array.isArray( selectedItems ) ) { + setSelected( [ ...selected, ...selectedItems ] ); + } + } } + onRemove={ ( removedItems ) => { + const newValues = Array.isArray( removedItems ) + ? selected.filter( + ( item ) => + ! removedItems.some( + ( { value: removedValue } ) => + item.value === removedValue + ) + ) + : selected.filter( + ( item ) => item.value !== removedItems.value + ); + setSelected( newValues ); + } } + /> + ); +}; + +export default { + title: 'WooCommerce Admin/experimental/SelectTreeControl', + component: SelectTree, +}; diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-expander.ts b/packages/js/components/src/experimental-tree-control/hooks/use-expander.ts index e7cf63433eb..02a9e5e54d3 100644 --- a/packages/js/components/src/experimental-tree-control/hooks/use-expander.ts +++ b/packages/js/components/src/experimental-tree-control/hooks/use-expander.ts @@ -17,7 +17,8 @@ export function useExpander( { useEffect( () => { if ( item.children?.length && - typeof shouldItemBeExpanded === 'function' + typeof shouldItemBeExpanded === 'function' && + ! isExpanded ) { setExpanded( shouldItemBeExpanded( item ) ); } diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-keyboard.ts b/packages/js/components/src/experimental-tree-control/hooks/use-keyboard.ts index e769975d85b..54db7555200 100644 --- a/packages/js/components/src/experimental-tree-control/hooks/use-keyboard.ts +++ b/packages/js/components/src/experimental-tree-control/hooks/use-keyboard.ts @@ -110,12 +110,14 @@ export function useKeyboard( { onExpand, onCollapse, onToggleExpand, + onLastItemLoop, }: { item: LinkedTree; isExpanded: boolean; onExpand(): void; onCollapse(): void; onToggleExpand(): void; + onLastItemLoop?( event: React.KeyboardEvent< HTMLDivElement > ): void; } ) { function onKeyDown( event: React.KeyboardEvent< HTMLDivElement > ) { if ( event.code === 'ArrowRight' ) { @@ -154,6 +156,9 @@ export function useKeyboard( { event.code ); element?.focus(); + if ( event.code === 'ArrowDown' && ! element && onLastItemLoop ) { + onLastItemLoop( event ); + } } if ( event.code === 'Home' ) { @@ -169,5 +174,5 @@ export function useKeyboard( { } } - return { onKeyDown }; + return { onKeyDown, onLastItemLoop }; } diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-selection.ts b/packages/js/components/src/experimental-tree-control/hooks/use-selection.ts index 255799940cc..79e47bfc9ec 100644 --- a/packages/js/components/src/experimental-tree-control/hooks/use-selection.ts +++ b/packages/js/components/src/experimental-tree-control/hooks/use-selection.ts @@ -75,6 +75,7 @@ function hasSelectedSibblingChildren( export function useSelection( { item, multiple, + shouldNotRecursivelySelect, selected, level, index, @@ -89,6 +90,7 @@ export function useSelection( { | 'index' | 'onSelect' | 'onRemove' + | 'shouldNotRecursivelySelect' > ) { const selectedItems = useMemo( () => { if ( level === 1 && index === 0 ) { @@ -100,7 +102,11 @@ export function useSelection( { const checkedStatus: CheckedStatus = useMemo( () => { if ( item.data.value in selectedItems ) { - if ( multiple && isIndeterminate( selectedItems, item.children ) ) { + if ( + multiple && + ! shouldNotRecursivelySelect && + isIndeterminate( selectedItems, item.children ) + ) { return 'indeterminate'; } return 'checked'; @@ -113,7 +119,7 @@ export function useSelection( { if ( multiple ) { value = [ item.data ]; - if ( item.children.length ) { + if ( item.children.length && ! shouldNotRecursivelySelect ) { value.push( ...getDeepChildren( item ) ); } } else if ( item.children?.length ) { @@ -132,7 +138,7 @@ export function useSelection( { function onSelectChildren( value: Item | Item[] ) { if ( typeof onSelect !== 'function' ) return; - if ( multiple ) { + if ( multiple && ! shouldNotRecursivelySelect ) { value = [ item.data, ...( value as Item[] ) ]; } @@ -142,7 +148,11 @@ export function useSelection( { function onRemoveChildren( value: Item | Item[] ) { if ( typeof onRemove !== 'function' ) return; - if ( multiple && item.children?.length ) { + if ( + multiple && + item.children?.length && + ! shouldNotRecursivelySelect + ) { const hasSelectedSibbling = hasSelectedSibblingChildren( item.children, value as Item[], diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-tree-item.ts b/packages/js/components/src/experimental-tree-control/hooks/use-tree-item.ts index 70f2a6d55db..35a08d19042 100644 --- a/packages/js/components/src/experimental-tree-control/hooks/use-tree-item.ts +++ b/packages/js/components/src/experimental-tree-control/hooks/use-tree-item.ts @@ -17,6 +17,7 @@ export function useTreeItem( { item, level, multiple, + shouldNotRecursivelySelect, selected, index, getLabel, @@ -24,6 +25,11 @@ export function useTreeItem( { shouldItemBeHighlighted, onSelect, onRemove, + isExpanded, + onCreateNew, + shouldShowCreateButton, + onLastItemLoop, + onTreeBlur, ...props }: TreeItemProps ) { const nextLevel = level + 1; @@ -41,6 +47,7 @@ export function useTreeItem( { index, onSelect, onRemove, + shouldNotRecursivelySelect, } ); const highlighter = useHighlighter( { @@ -56,6 +63,7 @@ export function useTreeItem( { const { onKeyDown } = useKeyboard( { ...expander, + onLastItemLoop, item, } ); @@ -96,6 +104,7 @@ export function useTreeItem( { getItemLabel: getLabel, shouldItemBeExpanded, shouldItemBeHighlighted, + shouldNotRecursivelySelect, onSelect: selection.onSelectChildren, onRemove: selection.onRemoveChildren, }, diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-tree.ts b/packages/js/components/src/experimental-tree-control/hooks/use-tree.ts index abba8a76ccd..656473e9456 100644 --- a/packages/js/components/src/experimental-tree-control/hooks/use-tree.ts +++ b/packages/js/components/src/experimental-tree-control/hooks/use-tree.ts @@ -8,7 +8,6 @@ import { TreeProps } from '../types'; export function useTree( { - ref, items, level = 1, role = 'tree', @@ -19,6 +18,11 @@ export function useTree( { shouldItemBeHighlighted, onSelect, onRemove, + shouldNotRecursivelySelect, + createValue, + onTreeBlur, + onCreateNew, + shouldShowCreateButton, ...props }: TreeProps ) { return { @@ -35,6 +39,7 @@ export function useTree( { getLabel: getItemLabel, shouldItemBeExpanded, shouldItemBeHighlighted, + shouldNotRecursivelySelect, onSelect, onRemove, }, diff --git a/packages/js/components/src/experimental-tree-control/tree.scss b/packages/js/components/src/experimental-tree-control/tree.scss index 221208658d5..f33dfe422a2 100644 --- a/packages/js/components/src/experimental-tree-control/tree.scss +++ b/packages/js/components/src/experimental-tree-control/tree.scss @@ -12,4 +12,13 @@ border: 1px solid $gray-400; border-radius: 2px; } + &__button { + width: 100%; + &:hover, + &:focus-within { + outline: 1.5px solid var( --wp-admin-theme-color ); + outline-offset: -1.5px; + background-color: $gray-100; + } + } } diff --git a/packages/js/components/src/experimental-tree-control/tree.tsx b/packages/js/components/src/experimental-tree-control/tree.tsx index 64a975481ca..491c6c161bd 100644 --- a/packages/js/components/src/experimental-tree-control/tree.tsx +++ b/packages/js/components/src/experimental-tree-control/tree.tsx @@ -1,8 +1,12 @@ /** * External dependencies */ +import { Button, Icon } from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; import classNames from 'classnames'; -import { createElement, forwardRef } from 'react'; +import { createElement, forwardRef, Fragment, useRef } from 'react'; +import { plus } from '@wordpress/icons'; +import { useMergeRefs } from '@wordpress/compose'; /** * Internal dependencies @@ -13,31 +17,93 @@ import { TreeProps } from './types'; export const Tree = forwardRef( function ForwardedTree( props: TreeProps, - ref: React.ForwardedRef< HTMLOListElement > + forwardedRef: React.ForwardedRef< HTMLOListElement > ) { + const rootListRef = useRef< HTMLOListElement >( null ); + const ref = useMergeRefs( [ rootListRef, forwardedRef ] ); + const { level, items, treeProps, treeItemProps } = useTree( { ...props, ref, } ); - if ( ! items.length ) return null; + const isCreateButtonVisible = + props.shouldShowCreateButton && + props.shouldShowCreateButton( props.createValue ); + return ( -
    +
      + { items.map( ( child, index ) => ( + { + ( + rootListRef.current + ?.closest( 'ol[role="tree"]' ) + ?.parentElement?.querySelector( + '.experimental-woocommerce-tree__button' + ) as HTMLButtonElement + )?.focus(); + } } + /> + ) ) } +
    + { isCreateButtonVisible && ( + ) } - > - { items.map( ( child, index ) => ( - - ) ) } -
+ ); } ); diff --git a/packages/js/components/src/experimental-tree-control/types.ts b/packages/js/components/src/experimental-tree-control/types.ts index bc25193e58e..8c60fd36194 100644 --- a/packages/js/components/src/experimental-tree-control/types.ts +++ b/packages/js/components/src/experimental-tree-control/types.ts @@ -14,7 +14,7 @@ export type CheckedStatus = 'checked' | 'unchecked' | 'indeterminate'; type BaseTreeProps = { /** - * It contians one item if `multiple` value is false or + * It contains one item if `multiple` value is false or * a list of items if it is true. */ selected?: Item | Item[]; @@ -22,6 +22,24 @@ type BaseTreeProps = { * Whether the tree items are single or multiple selected. */ multiple?: boolean; + /** + * In `multiple` mode, when this flag is also set, selecting children does + * not select their parents and selecting parents does not select their children. + */ + shouldNotRecursivelySelect?: boolean; + /** + * The value to be used for comparison to determine if 'create new' button should be shown. + */ + createValue?: string; + /** + * Called when the 'create new' button is clicked. + */ + onCreateNew?: () => void; + /** + * If passed, shows create button if return from callback is true + */ + shouldShowCreateButton?( value?: string ): boolean; + isExpanded?: boolean; /** * When `multiple` is true and a child item is selected, all its * ancestors and its descendants are also selected. If it's false @@ -54,6 +72,10 @@ type BaseTreeProps = { * @see {@link LinkedTree} */ shouldItemBeHighlighted?( item: LinkedTree ): boolean; + /** + * Called when the create button is clicked to help closing any related popover. + */ + onTreeBlur?(): void; }; export type TreeProps = BaseTreeProps & @@ -108,8 +130,10 @@ export type TreeItemProps = BaseTreeProps & level: number; item: LinkedTree; index: number; + isFocused?: boolean; getLabel?( item: LinkedTree ): JSX.Element; shouldItemBeExpanded?( item: LinkedTree ): boolean; + onLastItemLoop?( event: React.KeyboardEvent< HTMLDivElement > ): void; }; export type TreeControlProps = Omit< TreeProps, 'items' | 'level' > & { diff --git a/packages/js/components/src/index.ts b/packages/js/components/src/index.ts index eb0e63459ef..99d39bf3fa9 100644 --- a/packages/js/components/src/index.ts +++ b/packages/js/components/src/index.ts @@ -95,7 +95,11 @@ export { SlotContextType, SlotContextHelpersType, } from './slot-context'; -export { TreeControl as __experimentalTreeControl } from './experimental-tree-control'; +export { + TreeControl as __experimentalTreeControl, + Item as TreeItemType, +} from './experimental-tree-control'; +export { SelectTree as __experimentalSelectTreeControl } from './experimental-select-tree-control'; export { default as TreeSelectControl } from './tree-select-control'; // Exports below can be removed once the @woocommerce/product-editor package is released. diff --git a/packages/js/components/src/style.scss b/packages/js/components/src/style.scss index 613526c6549..d49f8b99b8a 100644 --- a/packages/js/components/src/style.scss +++ b/packages/js/components/src/style.scss @@ -56,5 +56,6 @@ @import 'collapsible-content/style.scss'; @import 'form/style.scss'; @import 'experimental-tree-control/tree.scss'; +@import 'experimental-select-tree-control/select-tree.scss'; @import 'product-section-layout/style.scss'; @import 'tree-select-control/index.scss'; From df52c85e92df2c2f0b7e2d01e239d1dff3b1bf91 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Mar 2023 19:16:51 +0100 Subject: [PATCH 037/466] Delete changelog files based on PR 37354 (#37356) Delete changelog files for 37354 Co-authored-by: WooCommerce Bot --- ...woocommerce_run_on_woocommerce_admin_updated-does-not-run | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 plugins/woocommerce/changelog/revert-36768-fix-36767-woocommerce_run_on_woocommerce_admin_updated-does-not-run diff --git a/plugins/woocommerce/changelog/revert-36768-fix-36767-woocommerce_run_on_woocommerce_admin_updated-does-not-run b/plugins/woocommerce/changelog/revert-36768-fix-36767-woocommerce_run_on_woocommerce_admin_updated-does-not-run deleted file mode 100644 index c48f20c0c5b..00000000000 --- a/plugins/woocommerce/changelog/revert-36768-fix-36767-woocommerce_run_on_woocommerce_admin_updated-does-not-run +++ /dev/null @@ -1,5 +0,0 @@ -Significance: patch -Type: fix -Comment: Revert #36768 - - From 0e1f974f49b0e4d731cb3b1fe9d3ebf86177b4a5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Mar 2023 19:17:54 +0100 Subject: [PATCH 038/466] Delete changelog files based on PR 37373 (#37380) Delete changelog files for 37373 Co-authored-by: WooCommerce Bot --- plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.2 | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.2 diff --git a/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.2 b/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.2 deleted file mode 100644 index eeb9356bb51..00000000000 --- a/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.2 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Update WooCommerce Blocks to 9.8.2 From a7516cb2b07228081168f98a61e73ececd03b53a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Mar 2023 19:18:43 +0100 Subject: [PATCH 039/466] Delete changelog files based on PR 36593 (#37420) Delete changelog files for 36593 Co-authored-by: WooCommerce Bot --- plugins/woocommerce/changelog/fix-35703-order-meta-backfill | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 plugins/woocommerce/changelog/fix-35703-order-meta-backfill diff --git a/plugins/woocommerce/changelog/fix-35703-order-meta-backfill b/plugins/woocommerce/changelog/fix-35703-order-meta-backfill deleted file mode 100644 index d6b48db0887..00000000000 --- a/plugins/woocommerce/changelog/fix-35703-order-meta-backfill +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -When order meta data is saved via HPOS, it should be backfilled to the CPT data store. From a8fff3175ccbb460dd0d1be1aefccf4488498105 Mon Sep 17 00:00:00 2001 From: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com> Date: Fri, 24 Mar 2023 12:07:49 -0700 Subject: [PATCH 040/466] Fix TypeScript Incremental Build Regression (#37432) --- .../changelog/fix-typescript-incremental-regression | 5 +++++ packages/js/components/tsconfig.json | 1 - .../changelog/fix-typescript-incremental-regression | 5 +++++ packages/js/csv-export/tsconfig.json | 1 - packages/js/internal-js-tests/tsconfig.json | 1 - packages/js/tsconfig.json | 1 - 6 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 packages/js/components/changelog/fix-typescript-incremental-regression create mode 100644 packages/js/csv-export/changelog/fix-typescript-incremental-regression diff --git a/packages/js/components/changelog/fix-typescript-incremental-regression b/packages/js/components/changelog/fix-typescript-incremental-regression new file mode 100644 index 00000000000..2d087939231 --- /dev/null +++ b/packages/js/components/changelog/fix-typescript-incremental-regression @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Configuration change only + + diff --git a/packages/js/components/tsconfig.json b/packages/js/components/tsconfig.json index a56e45f6af3..55b2cf3a463 100644 --- a/packages/js/components/tsconfig.json +++ b/packages/js/components/tsconfig.json @@ -6,7 +6,6 @@ "declaration": true, "declarationMap": true, "declarationDir": "./build-types", - "composite": true, "typeRoots": [ "./typings", "./node_modules/@types" diff --git a/packages/js/csv-export/changelog/fix-typescript-incremental-regression b/packages/js/csv-export/changelog/fix-typescript-incremental-regression new file mode 100644 index 00000000000..2d087939231 --- /dev/null +++ b/packages/js/csv-export/changelog/fix-typescript-incremental-regression @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Configuration change only + + diff --git a/packages/js/csv-export/tsconfig.json b/packages/js/csv-export/tsconfig.json index 6b4ebfddf82..c5f351a60cc 100644 --- a/packages/js/csv-export/tsconfig.json +++ b/packages/js/csv-export/tsconfig.json @@ -6,7 +6,6 @@ "declaration": true, "declarationMap": true, "declarationDir": "./build-types", - "composite": true, "typeRoots": [ "./typings", "./node_modules/@types" diff --git a/packages/js/internal-js-tests/tsconfig.json b/packages/js/internal-js-tests/tsconfig.json index e09ddc9d56e..37f13e9b816 100644 --- a/packages/js/internal-js-tests/tsconfig.json +++ b/packages/js/internal-js-tests/tsconfig.json @@ -3,7 +3,6 @@ "compilerOptions": { "rootDir": "src", "outDir": "build-module", - "composite": true, "typeRoots": [ "./typings", "./node_modules/@types" diff --git a/packages/js/tsconfig.json b/packages/js/tsconfig.json index b0bd9d3e2e4..6592fd74dc5 100644 --- a/packages/js/tsconfig.json +++ b/packages/js/tsconfig.json @@ -10,7 +10,6 @@ "jsx": "react", "jsxFactory": "createElement", "jsxFragmentFactory": "Fragment", - "incremental": true, "typeRoots": [] }, "exclude": [ From 62b275c41a86037ee8bf76f68fd6126176531340 Mon Sep 17 00:00:00 2001 From: Leif Singer Date: Sat, 25 Mar 2023 09:25:13 +0100 Subject: [PATCH 041/466] Update tested and required WordPress versions according to L-2 policy (#37434) --- .../changelog/dev-update-required-wordpress-version | 4 ++++ plugins/woocommerce/readme.txt | 4 ++-- plugins/woocommerce/woocommerce.php | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 plugins/woocommerce/changelog/dev-update-required-wordpress-version diff --git a/plugins/woocommerce/changelog/dev-update-required-wordpress-version b/plugins/woocommerce/changelog/dev-update-required-wordpress-version new file mode 100644 index 00000000000..46d408dca99 --- /dev/null +++ b/plugins/woocommerce/changelog/dev-update-required-wordpress-version @@ -0,0 +1,4 @@ +Significance: major +Type: dev + +Update tested and required WordPress versions according to L-2 policy. diff --git a/plugins/woocommerce/readme.txt b/plugins/woocommerce/readme.txt index efff28109f1..7c6381b276a 100644 --- a/plugins/woocommerce/readme.txt +++ b/plugins/woocommerce/readme.txt @@ -1,8 +1,8 @@ === WooCommerce === Contributors: automattic, mikejolley, jameskoster, claudiosanches, rodrigosprimo, peterfabian1000, vedjain, jamosova, obliviousharmony, konamiman, sadowski, wpmuguru, royho, barryhughes-1 Tags: online store, ecommerce, shop, shopping cart, sell online, storefront, checkout, payments, woo, woo commerce, e-commerce, store -Requires at least: 5.9 -Tested up to: 6.1 +Requires at least: 6.0 +Tested up to: 6.2 Requires PHP: 7.2 Stable tag: 7.5.1 License: GPLv3 diff --git a/plugins/woocommerce/woocommerce.php b/plugins/woocommerce/woocommerce.php index d900b75b4ac..50ccf0749f3 100644 --- a/plugins/woocommerce/woocommerce.php +++ b/plugins/woocommerce/woocommerce.php @@ -8,7 +8,7 @@ * Author URI: https://woocommerce.com * Text Domain: woocommerce * Domain Path: /i18n/languages/ - * Requires at least: 5.9 + * Requires at least: 6.0 * Requires PHP: 7.3 * * @package WooCommerce From 51e04b9eba7aedb34725744f50542d0235aa72da Mon Sep 17 00:00:00 2001 From: Justin Palmer Date: Sat, 25 Mar 2023 16:51:20 +0100 Subject: [PATCH 042/466] Remove multichannel marketing info from WC Tracker --- .../woocommerce/changelog/update-remove-multichannel-tracker | 4 ++++ plugins/woocommerce/includes/class-wc-tracker.php | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 plugins/woocommerce/changelog/update-remove-multichannel-tracker diff --git a/plugins/woocommerce/changelog/update-remove-multichannel-tracker b/plugins/woocommerce/changelog/update-remove-multichannel-tracker new file mode 100644 index 00000000000..d429a94b9d5 --- /dev/null +++ b/plugins/woocommerce/changelog/update-remove-multichannel-tracker @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Remove multichannel marketing info from WC Tracker diff --git a/plugins/woocommerce/includes/class-wc-tracker.php b/plugins/woocommerce/includes/class-wc-tracker.php index ed20ab4fa71..85320deae96 100644 --- a/plugins/woocommerce/includes/class-wc-tracker.php +++ b/plugins/woocommerce/includes/class-wc-tracker.php @@ -696,7 +696,6 @@ class WC_Tracker { 'hpos_transactions_enabled' => get_option( 'woocommerce_use_db_transactions_for_custom_orders_table_data_sync' ), 'hpos_transactions_level' => get_option( 'woocommerce_db_transactions_isolation_level_for_custom_orders_table_data_sync' ), 'show_marketplace_suggestions' => get_option( 'woocommerce_show_marketplace_suggestions' ), - 'multichannel_marketing_enabled' => get_option( 'woocommerce_multichannel_marketing_enabled' ), ); } From a77e4abffeb4677d911f76a2a60ff4f7a35b59c2 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Sun, 26 Mar 2023 21:42:33 -0400 Subject: [PATCH 043/466] Emit error on webpack build when invalid export name used in import for JS (#37195) --- .../changelog/update-webpack-invalid-export-error | 4 ++++ packages/js/admin-layout/webpack.config.js | 1 + .../components/changelog/update-webpack-invalid-export-error | 4 ++++ packages/js/components/webpack.config.js | 1 + .../changelog/update-webpack-invalid-export-error | 4 ++++ packages/js/customer-effort-score/webpack.config.js | 1 + .../changelog/update-webpack-invalid-export-error | 4 ++++ packages/js/experimental/webpack.config.js | 1 + packages/js/internal-style-build/index.js | 5 +++++ .../onboarding/changelog/update-webpack-invalid-export-error | 4 ++++ packages/js/onboarding/webpack.config.js | 1 + .../changelog/update-webpack-invalid-export-error | 4 ++++ packages/js/product-editor/webpack.config.js | 1 + plugins/woocommerce-admin/webpack.config.js | 1 + .../changelog/update-webpack-invalid-export-error | 4 ++++ 15 files changed, 40 insertions(+) create mode 100644 packages/js/admin-layout/changelog/update-webpack-invalid-export-error create mode 100644 packages/js/components/changelog/update-webpack-invalid-export-error create mode 100644 packages/js/customer-effort-score/changelog/update-webpack-invalid-export-error create mode 100644 packages/js/experimental/changelog/update-webpack-invalid-export-error create mode 100644 packages/js/onboarding/changelog/update-webpack-invalid-export-error create mode 100644 packages/js/product-editor/changelog/update-webpack-invalid-export-error create mode 100644 plugins/woocommerce/changelog/update-webpack-invalid-export-error diff --git a/packages/js/admin-layout/changelog/update-webpack-invalid-export-error b/packages/js/admin-layout/changelog/update-webpack-invalid-export-error new file mode 100644 index 00000000000..d844dbd186f --- /dev/null +++ b/packages/js/admin-layout/changelog/update-webpack-invalid-export-error @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update webpack config to use @woocommerce/internal-style-build's parser config diff --git a/packages/js/admin-layout/webpack.config.js b/packages/js/admin-layout/webpack.config.js index 46a04612713..9b42746f1dd 100644 --- a/packages/js/admin-layout/webpack.config.js +++ b/packages/js/admin-layout/webpack.config.js @@ -12,6 +12,7 @@ module.exports = { path: __dirname, }, module: { + parser: webpackConfig.parser, rules: webpackConfig.rules, }, plugins: webpackConfig.plugins, diff --git a/packages/js/components/changelog/update-webpack-invalid-export-error b/packages/js/components/changelog/update-webpack-invalid-export-error new file mode 100644 index 00000000000..d844dbd186f --- /dev/null +++ b/packages/js/components/changelog/update-webpack-invalid-export-error @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update webpack config to use @woocommerce/internal-style-build's parser config diff --git a/packages/js/components/webpack.config.js b/packages/js/components/webpack.config.js index 46a04612713..9b42746f1dd 100644 --- a/packages/js/components/webpack.config.js +++ b/packages/js/components/webpack.config.js @@ -12,6 +12,7 @@ module.exports = { path: __dirname, }, module: { + parser: webpackConfig.parser, rules: webpackConfig.rules, }, plugins: webpackConfig.plugins, diff --git a/packages/js/customer-effort-score/changelog/update-webpack-invalid-export-error b/packages/js/customer-effort-score/changelog/update-webpack-invalid-export-error new file mode 100644 index 00000000000..d844dbd186f --- /dev/null +++ b/packages/js/customer-effort-score/changelog/update-webpack-invalid-export-error @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update webpack config to use @woocommerce/internal-style-build's parser config diff --git a/packages/js/customer-effort-score/webpack.config.js b/packages/js/customer-effort-score/webpack.config.js index 46a04612713..9b42746f1dd 100644 --- a/packages/js/customer-effort-score/webpack.config.js +++ b/packages/js/customer-effort-score/webpack.config.js @@ -12,6 +12,7 @@ module.exports = { path: __dirname, }, module: { + parser: webpackConfig.parser, rules: webpackConfig.rules, }, plugins: webpackConfig.plugins, diff --git a/packages/js/experimental/changelog/update-webpack-invalid-export-error b/packages/js/experimental/changelog/update-webpack-invalid-export-error new file mode 100644 index 00000000000..d844dbd186f --- /dev/null +++ b/packages/js/experimental/changelog/update-webpack-invalid-export-error @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update webpack config to use @woocommerce/internal-style-build's parser config diff --git a/packages/js/experimental/webpack.config.js b/packages/js/experimental/webpack.config.js index 46a04612713..9b42746f1dd 100644 --- a/packages/js/experimental/webpack.config.js +++ b/packages/js/experimental/webpack.config.js @@ -12,6 +12,7 @@ module.exports = { path: __dirname, }, module: { + parser: webpackConfig.parser, rules: webpackConfig.rules, }, plugins: webpackConfig.plugins, diff --git a/packages/js/internal-style-build/index.js b/packages/js/internal-style-build/index.js index 5443e844a92..a3f03feb3e0 100644 --- a/packages/js/internal-style-build/index.js +++ b/packages/js/internal-style-build/index.js @@ -11,6 +11,11 @@ const NODE_ENV = process.env.NODE_ENV || 'development'; module.exports = { webpackConfig: { + parser: { + javascript: { + exportsPresence: 'error', + }, + }, rules: [ { test: /\.s?css$/, diff --git a/packages/js/onboarding/changelog/update-webpack-invalid-export-error b/packages/js/onboarding/changelog/update-webpack-invalid-export-error new file mode 100644 index 00000000000..d844dbd186f --- /dev/null +++ b/packages/js/onboarding/changelog/update-webpack-invalid-export-error @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update webpack config to use @woocommerce/internal-style-build's parser config diff --git a/packages/js/onboarding/webpack.config.js b/packages/js/onboarding/webpack.config.js index 46a04612713..9b42746f1dd 100644 --- a/packages/js/onboarding/webpack.config.js +++ b/packages/js/onboarding/webpack.config.js @@ -12,6 +12,7 @@ module.exports = { path: __dirname, }, module: { + parser: webpackConfig.parser, rules: webpackConfig.rules, }, plugins: webpackConfig.plugins, diff --git a/packages/js/product-editor/changelog/update-webpack-invalid-export-error b/packages/js/product-editor/changelog/update-webpack-invalid-export-error new file mode 100644 index 00000000000..d844dbd186f --- /dev/null +++ b/packages/js/product-editor/changelog/update-webpack-invalid-export-error @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update webpack config to use @woocommerce/internal-style-build's parser config diff --git a/packages/js/product-editor/webpack.config.js b/packages/js/product-editor/webpack.config.js index 46a04612713..9b42746f1dd 100644 --- a/packages/js/product-editor/webpack.config.js +++ b/packages/js/product-editor/webpack.config.js @@ -12,6 +12,7 @@ module.exports = { path: __dirname, }, module: { + parser: webpackConfig.parser, rules: webpackConfig.rules, }, plugins: webpackConfig.plugins, diff --git a/plugins/woocommerce-admin/webpack.config.js b/plugins/woocommerce-admin/webpack.config.js index 54a6afebd25..ab3ea444347 100644 --- a/plugins/woocommerce-admin/webpack.config.js +++ b/plugins/woocommerce-admin/webpack.config.js @@ -104,6 +104,7 @@ const webpackConfig = { uniqueName: '__wcAdmin_webpackJsonp', }, module: { + parser: styleConfig.parser, rules: [ { test: /\.(t|j)sx?$/, diff --git a/plugins/woocommerce/changelog/update-webpack-invalid-export-error b/plugins/woocommerce/changelog/update-webpack-invalid-export-error new file mode 100644 index 00000000000..d844dbd186f --- /dev/null +++ b/plugins/woocommerce/changelog/update-webpack-invalid-export-error @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update webpack config to use @woocommerce/internal-style-build's parser config From de111c1edb6bb1ca348526622a385bfae29b6f5d Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Mon, 27 Mar 2023 07:09:39 -0400 Subject: [PATCH 044/466] Update TourKit README to correct primaryButton example and formatting (#37427) --- .../changelog/fix-tour-kit-documentation | 4 + packages/js/components/src/tour-kit/README.md | 87 ++++++++++--------- 2 files changed, 49 insertions(+), 42 deletions(-) create mode 100644 packages/js/components/changelog/fix-tour-kit-documentation diff --git a/packages/js/components/changelog/fix-tour-kit-documentation b/packages/js/components/changelog/fix-tour-kit-documentation new file mode 100644 index 00000000000..d2df9ede44b --- /dev/null +++ b/packages/js/components/changelog/fix-tour-kit-documentation @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Update TourKit README to correct primaryButton example and formatting. diff --git a/packages/js/components/src/tour-kit/README.md b/packages/js/components/src/tour-kit/README.md index c1f52a2920f..a27d0172272 100644 --- a/packages/js/components/src/tour-kit/README.md +++ b/packages/js/components/src/tour-kit/README.md @@ -28,13 +28,15 @@ function Tour() { referenceElements: { desktop: '.render-step-near-me', }, - meta: { - heading: 'Lorem ipsum dolor sit amet.', - descriptions: { - desktop: 'Lorem ipsum dolor sit amet.', - }, - primaryButtonText: "Done" - }, + meta: { + heading: 'Lorem ipsum dolor sit amet.', + descriptions: { + desktop: 'Lorem ipsum dolor sit amet.', + }, + primaryButton: { + text: 'Done', + }, + }, }, ], closeHandler: () => setShowTour( false ), @@ -57,8 +59,8 @@ function Tour() { When a tour is rendered and focused, the following functionality exists: -- Close the tour on `ESC` key (in minimized view) -- Go to previous/next step on `left/right` arrow keys (in step view) +- Close the tour on `ESC` key (in minimized view) +- Go to previous/next step on `left/right` arrow keys (in step view) ## Configuration @@ -66,51 +68,52 @@ The main API for configuring a tour is the config object. See example usage and `config.steps`: An array of objects that define the content we wish to render on the page. Each step defined by: -- `referenceElements` (optional): A set of `desktop` & `mobile` selectors to render the step near. -- `focusElement` (optional): A set of `desktop` & `mobile` & `iframe` selectors to automatically focus. -- `meta`: Arbitrary object that encloses the content we want to render for each step. -- `classNames` (optional): An array or CSV of CSS classes applied to a step. +- `referenceElements` (optional): A set of `desktop` & `mobile` selectors to render the step near. +- `focusElement` (optional): A set of `desktop` & `mobile` & `iframe` selectors to automatically focus. +- `meta`: Arbitrary object that encloses the content we want to render for each step. +- `classNames` (optional): An array or CSV of CSS classes applied to a step. `config.closeHandler`: The callback responsible for closing the tour. -- `tourStep`: A React component that will be called to render each step. Receives the following properties: +- `tourStep`: A React component that will be called to render each step. Receives the following properties: - - `steps`: The steps defined for the tour. - - `currentStepIndex` - - `onDismiss`: Handler that dismissed/closes the tour. - - `onNext`: Handler that progresses the tour to the next step. - - `onPrevious`: Handler that takes the tour to the previous step. - - `onMinimize`: Handler that minimizes the tour (passes rendering to `tourMinimized`). - - `setInitialFocusedElement`: A dispatcher that assigns an element to be initially focused when a step renders (see examples). - - `onGoToStep`: Handler that progresses the tour to a given step index. + - `steps`: The steps defined for the tour. + - `currentStepIndex` + - `onDismiss`: Handler that dismissed/closes the tour. + - `onNext`: Handler that progresses the tour to the next step. + - `onPrevious`: Handler that takes the tour to the previous step. + - `onMinimize`: Handler that minimizes the tour (passes rendering to `tourMinimized`). + - `setInitialFocusedElement`: A dispatcher that assigns an element to be initially focused when a step renders (see examples). + - `onGoToStep`: Handler that progresses the tour to a given step index. -- `tourMinimized`: A React component that will be called to render a minimized view for the tour. Receives the following properties: - - `steps`: The steps defined for the tour. - - `currentStepIndex` - - `onDismiss`: Handler that dismissed/closes the tour. - - `onMaximize`: Handler that expands the tour (passes rendering to `tourStep`). +- `tourMinimized`: A React component that will be called to render a minimized view for the tour. Receives the following properties: + - `steps`: The steps defined for the tour. + - `currentStepIndex` + - `onDismiss`: Handler that dismissed/closes the tour. + - `onMaximize`: Handler that expands the tour (passes rendering to `tourStep`). `config.options` (optional): -- `classNames` (optional): An array or CSV of CSS classes to enclose the main tour frame with. +- `classNames` (optional): An array or CSV of CSS classes to enclose the main tour frame with. -- `effects`: An object to enable/disable/combine various tour effects: +- `effects`: An object to enable/disable/combine various tour effects: - - `spotlight`: Adds a semi-transparent overlay and highlights the reference element when provided with a transparent box over it. Expects an object with optional styles to override the default highlight/spotlight behavior when provided (default: spotlight wraps the entire reference element). - - `interactivity`: An object that configures whether the user is allowed to interact with the referenced element during the tour - - `styles`: CSS properties that configures the styles applied to the spotlight overlay - - `arrowIndicator`: Adds an arrow tip pointing at the reference element when provided. - - `overlay`: Includes the semi-transparent overlay for all the steps (also blocks interactions with the rest of the page) - - `autoScroll`: The page scrolls up and down automatically such that the step target element is visible to the user. + - `spotlight`: Adds a semi-transparent overlay and highlights the reference element when provided with a transparent box over it. Expects an object with optional styles to override the default highlight/spotlight behavior when provided (default: spotlight wraps the entire reference element). + - `interactivity`: An object that configures whether the user is allowed to interact with the referenced element during the tour + - `styles`: CSS properties that configures the styles applied to the spotlight overlay + - `arrowIndicator`: Adds an arrow tip pointing at the reference element when provided. + - `overlay`: Includes the semi-transparent overlay for all the steps (also blocks interactions with the rest of the page) + - `autoScroll`: The page scrolls up and down automatically such that the step target element is visible to the user. -- `callbacks`: An object of callbacks to handle side effects from various interactions (see [types.ts](./src/types.ts)). +- `callbacks`: An object of callbacks to handle side effects from various interactions (see [types.ts](./src/types.ts)). -- `popperModifiers`: The tour uses Popper to position steps near reference elements (and for other effects). An implementation can pass its own modifiers to tailor the functionality further e.g. more offset or padding from the reference element. -- `tourRating` (optional - only in WPCOM Tour Kit variant): - - `enabled`: Whether to show rating in last step. - - `useTourRating`: (optional) A hook to provide the rating from an external source/state (see [types.ts](./src/types.ts)). - - `onTourRate`: (optional) A callback to fire off when a rating is submitted. +- `popperModifiers`: The tour uses Popper to position steps near reference elements (and for other effects). An implementation can pass its own modifiers to tailor the functionality further e.g. more offset or padding from the reference element. +- `tourRating` (optional - only in WPCOM Tour Kit variant): -- `portalElementId`: A string that lets you customize under which DOM element the Tour will be appended. + - `enabled`: Whether to show rating in last step. + - `useTourRating`: (optional) A hook to provide the rating from an external source/state (see [types.ts](./src/types.ts)). + - `onTourRate`: (optional) A callback to fire off when a rating is submitted. + +- `portalElementId`: A string that lets you customize under which DOM element the Tour will be appended. `placement` (Optional) : Describes the preferred placement of the popper. Possible values are left-start, left, left-end, top-start, top, top-end, right-start, right, right-end, bottom-start, bottom, and bottom-end. From 48c50afcbf1dd16c5dff02bb35d5ef173d53ecce Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Fri, 24 Mar 2023 21:28:38 +0800 Subject: [PATCH 045/466] Move RecommendedExtensions and KnowledgeBase into coupons directory. --- plugins/woocommerce-admin/client/marketing/coupons/index.js | 4 ++-- .../marketing/{components => coupons}/knowledge-base/index.js | 0 .../{components => coupons}/knowledge-base/placeholder.js | 0 .../{components => coupons}/knowledge-base/style.scss | 0 .../{components => coupons}/knowledge-base/test/index.js | 0 .../{components => coupons}/recommended-extensions/index.js | 2 +- .../{components => coupons}/recommended-extensions/item.js | 2 +- .../recommended-extensions/placeholder.js | 0 .../{components => coupons}/recommended-extensions/style.scss | 0 .../test/__snapshots__/placeholder.js.snap | 0 .../recommended-extensions/test/index.js | 0 .../recommended-extensions/test/placeholder.js | 2 +- 12 files changed, 5 insertions(+), 5 deletions(-) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/knowledge-base/index.js (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/knowledge-base/placeholder.js (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/knowledge-base/style.scss (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/knowledge-base/test/index.js (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/recommended-extensions/index.js (98%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/recommended-extensions/item.js (96%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/recommended-extensions/placeholder.js (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/recommended-extensions/style.scss (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/recommended-extensions/test/__snapshots__/placeholder.js.snap (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/recommended-extensions/test/index.js (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/recommended-extensions/test/placeholder.js (87%) diff --git a/plugins/woocommerce-admin/client/marketing/coupons/index.js b/plugins/woocommerce-admin/client/marketing/coupons/index.js index ccce6d94d74..4b2b2cb8af8 100644 --- a/plugins/woocommerce-admin/client/marketing/coupons/index.js +++ b/plugins/woocommerce-admin/client/marketing/coupons/index.js @@ -8,8 +8,8 @@ import { useUser } from '@woocommerce/data'; * Internal dependencies */ import './style.scss'; -import RecommendedExtensions from '../components/recommended-extensions'; -import KnowledgeBase from '../components/knowledge-base'; +import RecommendedExtensions from './recommended-extensions'; +import KnowledgeBase from './knowledge-base'; import { getAdminSetting } from '~/utils/admin-settings'; import '../data'; diff --git a/plugins/woocommerce-admin/client/marketing/components/knowledge-base/index.js b/plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/index.js similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/knowledge-base/index.js rename to plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/index.js diff --git a/plugins/woocommerce-admin/client/marketing/components/knowledge-base/placeholder.js b/plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/placeholder.js similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/knowledge-base/placeholder.js rename to plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/placeholder.js diff --git a/plugins/woocommerce-admin/client/marketing/components/knowledge-base/style.scss b/plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/style.scss similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/knowledge-base/style.scss rename to plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/style.scss diff --git a/plugins/woocommerce-admin/client/marketing/components/knowledge-base/test/index.js b/plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/test/index.js similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/knowledge-base/test/index.js rename to plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/test/index.js diff --git a/plugins/woocommerce-admin/client/marketing/components/recommended-extensions/index.js b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/index.js similarity index 98% rename from plugins/woocommerce-admin/client/marketing/components/recommended-extensions/index.js rename to plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/index.js index 0286256683e..6c0b78cb563 100644 --- a/plugins/woocommerce-admin/client/marketing/components/recommended-extensions/index.js +++ b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/index.js @@ -14,7 +14,7 @@ import './style.scss'; import RecommendedExtensionsItem from './item'; import RecommendedExtensionsPlaceholder from './placeholder'; import { STORE_KEY } from '../../data/constants'; -import Card from '../card'; +import Card from '../../components/card'; const RecommendedExtensions = ( { extensions, diff --git a/plugins/woocommerce-admin/client/marketing/components/recommended-extensions/item.js b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/item.js similarity index 96% rename from plugins/woocommerce-admin/client/marketing/components/recommended-extensions/item.js rename to plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/item.js index 1a4d6cfeea5..02327d0fc5a 100644 --- a/plugins/woocommerce-admin/client/marketing/components/recommended-extensions/item.js +++ b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/item.js @@ -8,7 +8,7 @@ import { recordEvent } from '@woocommerce/tracks'; * Internal dependencies */ import './style.scss'; -import { ProductIcon } from '../../components/'; +import { ProductIcon } from '../../components'; import { getInAppPurchaseUrl } from '../../../lib/in-app-purchase'; const RecommendedExtensionsItem = ( { diff --git a/plugins/woocommerce-admin/client/marketing/components/recommended-extensions/placeholder.js b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/placeholder.js similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/recommended-extensions/placeholder.js rename to plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/placeholder.js diff --git a/plugins/woocommerce-admin/client/marketing/components/recommended-extensions/style.scss b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/style.scss similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/recommended-extensions/style.scss rename to plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/style.scss diff --git a/plugins/woocommerce-admin/client/marketing/components/recommended-extensions/test/__snapshots__/placeholder.js.snap b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/test/__snapshots__/placeholder.js.snap similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/recommended-extensions/test/__snapshots__/placeholder.js.snap rename to plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/test/__snapshots__/placeholder.js.snap diff --git a/plugins/woocommerce-admin/client/marketing/components/recommended-extensions/test/index.js b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/test/index.js similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/recommended-extensions/test/index.js rename to plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/test/index.js diff --git a/plugins/woocommerce-admin/client/marketing/components/recommended-extensions/test/placeholder.js b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/test/placeholder.js similarity index 87% rename from plugins/woocommerce-admin/client/marketing/components/recommended-extensions/test/placeholder.js rename to plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/test/placeholder.js index df2cfe4612e..e6c464e2941 100644 --- a/plugins/woocommerce-admin/client/marketing/components/recommended-extensions/test/placeholder.js +++ b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/test/placeholder.js @@ -7,7 +7,7 @@ import { createElement } from '@wordpress/element'; /** * Internal dependencies */ -import { RecommendedExtensionsPlaceholder } from '../'; +import { RecommendedExtensionsPlaceholder } from '..'; describe( 'RecommendedExtensionsPlaceholder', () => { test( 'should render a default placeholder', () => { From ab2d56d8b343cbda78632a164e5cb8113e41f095 Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Fri, 24 Mar 2023 21:45:25 +0800 Subject: [PATCH 046/466] Move card and slider into coupons directory. --- .../woocommerce-admin/client/marketing/components/index.js | 2 -- .../client/marketing/{components => coupons}/card/README.md | 0 .../client/marketing/{components => coupons}/card/index.js | 0 .../marketing/{components => coupons}/card/style.scss | 0 .../marketing/{components => coupons}/card/test/index.js | 0 .../client/marketing/coupons/knowledge-base/index.js | 6 ++++-- .../marketing/coupons/recommended-extensions/index.js | 4 ++-- .../marketing/{components => coupons}/slider/index.js | 0 .../marketing/{components => coupons}/slider/style.scss | 0 9 files changed, 6 insertions(+), 6 deletions(-) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/card/README.md (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/card/index.js (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/card/style.scss (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/card/test/index.js (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/slider/index.js (100%) rename plugins/woocommerce-admin/client/marketing/{components => coupons}/slider/style.scss (100%) diff --git a/plugins/woocommerce-admin/client/marketing/components/index.js b/plugins/woocommerce-admin/client/marketing/components/index.js index c26b16cb695..69b332786f3 100644 --- a/plugins/woocommerce-admin/client/marketing/components/index.js +++ b/plugins/woocommerce-admin/client/marketing/components/index.js @@ -1,6 +1,4 @@ -export { default as Card } from './card'; export { default as ProductIcon } from './product-icon'; -export { default as Slider } from './slider'; export { default as ReadBlogMessage } from './ReadBlogMessage'; export { CollapsibleCard, CardBody, CardDivider } from './CollapsibleCard'; export { PluginCardBody, SmartPluginCardBody } from './PluginCardBody'; diff --git a/plugins/woocommerce-admin/client/marketing/components/card/README.md b/plugins/woocommerce-admin/client/marketing/coupons/card/README.md similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/card/README.md rename to plugins/woocommerce-admin/client/marketing/coupons/card/README.md diff --git a/plugins/woocommerce-admin/client/marketing/components/card/index.js b/plugins/woocommerce-admin/client/marketing/coupons/card/index.js similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/card/index.js rename to plugins/woocommerce-admin/client/marketing/coupons/card/index.js diff --git a/plugins/woocommerce-admin/client/marketing/components/card/style.scss b/plugins/woocommerce-admin/client/marketing/coupons/card/style.scss similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/card/style.scss rename to plugins/woocommerce-admin/client/marketing/coupons/card/style.scss diff --git a/plugins/woocommerce-admin/client/marketing/components/card/test/index.js b/plugins/woocommerce-admin/client/marketing/coupons/card/test/index.js similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/card/test/index.js rename to plugins/woocommerce-admin/client/marketing/coupons/card/test/index.js diff --git a/plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/index.js b/plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/index.js index cb74eae7807..d48205df56f 100644 --- a/plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/index.js +++ b/plugins/woocommerce-admin/client/marketing/coupons/knowledge-base/index.js @@ -14,8 +14,10 @@ import { recordEvent } from '@woocommerce/tracks'; * Internal dependencies */ import './style.scss'; -import { Card, Slider, ReadBlogMessage } from '../../components'; -import { STORE_KEY } from '../../data/constants'; +import { STORE_KEY } from '~/marketing/data/constants'; +import { ReadBlogMessage } from '~/marketing/components'; +import Card from '../card'; +import Slider from '../slider'; import KnowledgebaseCardPostPlaceholder from './placeholder'; const KnowledgeBase = ( { diff --git a/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/index.js b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/index.js index 6c0b78cb563..8f606448c95 100644 --- a/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/index.js +++ b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/index.js @@ -13,8 +13,8 @@ import PropTypes from 'prop-types'; import './style.scss'; import RecommendedExtensionsItem from './item'; import RecommendedExtensionsPlaceholder from './placeholder'; -import { STORE_KEY } from '../../data/constants'; -import Card from '../../components/card'; +import { STORE_KEY } from '~/marketing/data/constants'; +import Card from '../card'; const RecommendedExtensions = ( { extensions, diff --git a/plugins/woocommerce-admin/client/marketing/components/slider/index.js b/plugins/woocommerce-admin/client/marketing/coupons/slider/index.js similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/slider/index.js rename to plugins/woocommerce-admin/client/marketing/coupons/slider/index.js diff --git a/plugins/woocommerce-admin/client/marketing/components/slider/style.scss b/plugins/woocommerce-admin/client/marketing/coupons/slider/style.scss similarity index 100% rename from plugins/woocommerce-admin/client/marketing/components/slider/style.scss rename to plugins/woocommerce-admin/client/marketing/coupons/slider/style.scss From dd2fe3869bc4d8563f1ad6d6cb128d27ec7f6620 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Mon, 27 Mar 2023 17:44:52 +0530 Subject: [PATCH 047/466] Add support for end_at ID to allow partial verification. --- .../Migrations/CustomOrderTable/CLIRunner.php | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php index ccc17741a33..4e3cf52c14c 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php @@ -288,10 +288,16 @@ class CLIRunner { * default: 0 * --- * + * [--end-at=] + * : Order ID to end at. + * --- + * default: -1 + * --- + * * ## EXAMPLES * * # Verify migrated order data, 500 orders at a time. - * wp wc cot verify_cot_data --batch-size=500 --start-from=0 + * wp wc cot verify_cot_data --batch-size=500 --start-from=0 --end-at=10000 * * @param array $args Positional arguments passed to the command. * @param array $assoc_args Associative arguments (options) passed to the command. @@ -308,6 +314,7 @@ class CLIRunner { array( 'batch-size' => 500, 'start-from' => 0, + 'end-at' => -1, ) ); @@ -316,7 +323,9 @@ class CLIRunner { $failed_ids = array(); $processed = 0; $order_id_start = (int) $assoc_args['start-from']; - $order_count = $this->get_verify_order_count( $order_id_start ); + $order_id_end = (int) $assoc_args['end-at']; + $order_id_end = $order_id_end === - 1 ? PHP_INT_MAX : $order_id_end; + $order_count = $this->get_verify_order_count( $order_id_start, $order_id_end ); $batch_size = ( (int) $assoc_args['batch-size'] ) === 0 ? 500 : (int) $assoc_args['batch-size']; $progress = WP_CLI\Utils\make_progress_bar( 'Order Data Verification', $order_count / $batch_size ); @@ -337,8 +346,9 @@ class CLIRunner { $order_ids = $wpdb->get_col( $wpdb->prepare( - "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order' AND ID > %d ORDER BY ID ASC LIMIT %d", + "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order' AND ID > %d AND ID < %d ORDER BY ID ASC LIMIT %d", $order_id_start, + $order_id_end, $batch_size ) ); @@ -424,17 +434,19 @@ class CLIRunner { * Helper method to get count for orders needing verification. * * @param int $order_id_start Order ID to start from. + * @param int $order_id_end Order ID to end at. * @param bool $log Whether to also log an error message. * * @return int Order count. */ - private function get_verify_order_count( int $order_id_start, $log = true ) : int { + private function get_verify_order_count( int $order_id_start, int $order_id_end, $log = true ) : int { global $wpdb; $order_count = (int) $wpdb->get_var( $wpdb->prepare( - "SELECT COUNT(*) FROM $wpdb->posts WHERE post_type = 'shop_order' AND ID > %d", - $order_id_start + "SELECT COUNT(*) FROM $wpdb->posts WHERE post_type = 'shop_order' AND ID > %d AND ID < %d", + $order_id_start, + $order_id_end ) ); From bb0ca9184659b533d763ff92f0a1c4688ea4a4b2 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Mon, 27 Mar 2023 17:45:44 +0530 Subject: [PATCH 048/466] Add changelog. --- plugins/woocommerce/changelog/hpos-end_at_support | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/hpos-end_at_support diff --git a/plugins/woocommerce/changelog/hpos-end_at_support b/plugins/woocommerce/changelog/hpos-end_at_support new file mode 100644 index 00000000000..e73f5dd38f3 --- /dev/null +++ b/plugins/woocommerce/changelog/hpos-end_at_support @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Add support for end_at ID to allow partial verification. From 64bd683e0b335bb3dd714c49d002f5a86fee3907 Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Mon, 27 Mar 2023 20:17:05 +0800 Subject: [PATCH 049/466] Simplify import. --- .../client/marketing/coupons/recommended-extensions/item.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/item.js b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/item.js index 02327d0fc5a..395dfe5555c 100644 --- a/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/item.js +++ b/plugins/woocommerce-admin/client/marketing/coupons/recommended-extensions/item.js @@ -8,8 +8,8 @@ import { recordEvent } from '@woocommerce/tracks'; * Internal dependencies */ import './style.scss'; -import { ProductIcon } from '../../components'; -import { getInAppPurchaseUrl } from '../../../lib/in-app-purchase'; +import { ProductIcon } from '~/marketing/components'; +import { getInAppPurchaseUrl } from '~/lib/in-app-purchase'; const RecommendedExtensionsItem = ( { title, From 07181dd1425f5a345a17d47cd4caf9d14aac545c Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Mon, 27 Mar 2023 20:27:32 +0800 Subject: [PATCH 050/466] Add changelog. --- .../changelog/feature-marketing-refactor-components | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/feature-marketing-refactor-components diff --git a/plugins/woocommerce/changelog/feature-marketing-refactor-components b/plugins/woocommerce/changelog/feature-marketing-refactor-components new file mode 100644 index 00000000000..17d9307e65d --- /dev/null +++ b/plugins/woocommerce/changelog/feature-marketing-refactor-components @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Code refactor on marketing components. From cf9fbb22485c8d25a332b51dfbe7cfa20bda1f25 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 27 Mar 2023 15:39:49 +0200 Subject: [PATCH 051/466] Updated accessibility for product categories shortcode. --- plugins/woocommerce/includes/wc-template-functions.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/woocommerce/includes/wc-template-functions.php b/plugins/woocommerce/includes/wc-template-functions.php index c376d601e47..a069ecc3c28 100644 --- a/plugins/woocommerce/includes/wc-template-functions.php +++ b/plugins/woocommerce/includes/wc-template-functions.php @@ -1223,7 +1223,10 @@ if ( ! function_exists( 'woocommerce_template_loop_category_link_open' ) ) { * @param int|object|string $category Category ID, Object or String. */ function woocommerce_template_loop_category_link_open( $category ) { - echo ''; + $category_term = get_term( $category, 'product_cat' ); + $category_name = ( ! $category_term || is_wp_error( $category_term ) ) ? '' : $category_term->name; + /* translators: %s: Category name */ + echo ''; } } From e8409660da6e06152d11f8ecbc3fd2916c6a1432 Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 27 Mar 2023 15:40:31 +0200 Subject: [PATCH 052/466] Changelog. --- plugins/woocommerce/changelog/fix-34109 | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/fix-34109 diff --git a/plugins/woocommerce/changelog/fix-34109 b/plugins/woocommerce/changelog/fix-34109 new file mode 100644 index 00000000000..36fe6df1707 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-34109 @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Accessibility update for product_categories shortcode. From de2aa280ef94c5307d487dde3b4ac6d663257c3e Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Mon, 27 Mar 2023 21:55:42 +0800 Subject: [PATCH 053/466] Simplify boolean expression before && in Marketing page. --- .../CreateNewCampaignModal.tsx | 9 +++-- .../client/marketing/coupons/index.js | 7 ++-- .../Campaigns/Campaigns.tsx | 6 ++-- .../Channels/Channels.tsx | 4 +-- .../IntroductionBanner/IntroductionBanner.tsx | 5 +-- .../MarketingOverviewMultichannel.tsx | 34 +++++++++++-------- 6 files changed, 39 insertions(+), 26 deletions(-) diff --git a/plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/CreateNewCampaignModal.tsx b/plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/CreateNewCampaignModal.tsx index 09ebf5aa7a5..2df5a402104 100644 --- a/plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/CreateNewCampaignModal.tsx +++ b/plugins/woocommerce-admin/client/marketing/components/CreateNewCampaignModal/CreateNewCampaignModal.tsx @@ -47,6 +47,9 @@ export const CreateNewCampaignModal = ( props: CreateCampaignModalProps ) => { const { loadInstalledPluginsAfterActivation } = useInstalledPluginsWithoutChannels(); + const hasCampaignTypes = !! campaignTypes?.length; + const hasRecommendedChannels = !! recommendedChannels?.length; + const onInstalledAndActivated = ( pluginSlug: string ) => { refetchCampaignTypes(); refetchRegisteredChannels(); @@ -64,7 +67,7 @@ export const CreateNewCampaignModal = ( props: CreateCampaignModalProps ) => { >
- { !! campaignTypes?.length + { hasCampaignTypes ? __( 'Where would you like to promote your products?', 'woocommerce' @@ -109,7 +112,7 @@ export const CreateNewCampaignModal = ( props: CreateCampaignModalProps ) => { { __( 'Create', 'woocommerce' ) } - { !! isExternalURL( el.createUrl ) && ( + { isExternalURL( el.createUrl ) && ( { ) ) }
- { !! recommendedChannels?.length && ( + { hasRecommendedChannels && (
diff --git a/plugins/woocommerce-admin/client/marketing/coupons/index.js b/plugins/woocommerce-admin/client/marketing/coupons/index.js index 4b2b2cb8af8..d817b832a2e 100644 --- a/plugins/woocommerce-admin/client/marketing/coupons/index.js +++ b/plugins/woocommerce-admin/client/marketing/coupons/index.js @@ -16,13 +16,14 @@ import '../data'; const CouponsOverview = () => { const { currentUserCan } = useUser(); - const shouldShowExtensions = + const showExtensions = !! ( getAdminSetting( 'allowMarketplaceSuggestions', false ) && - currentUserCan( 'install_plugins' ); + currentUserCan( 'install_plugins' ) + ); return (
- { !! shouldShowExtensions && ( + { showExtensions && ( { ); }; + const showFooter = !! ( total && total > perPage ); + return ( @@ -170,14 +172,14 @@ export const Campaigns = () => { > { __( 'Create new campaign', 'woocommerce' ) } - { !! isModalOpen && ( + { isModalOpen && ( setModalOpen( false ) } /> ) } { getContent() } - { !! ( total && total > perPage ) && ( + { showFooter && ( ( { /* Recommended channels section. */ } { recommendedChannels.length >= 1 && (
- { !! hasRegisteredChannels && ( + { hasRegisteredChannels && ( <> @@ -127,7 +127,7 @@ export const Channels = forwardRef< ChannelsRef, ChannelsProps >( ) } - { !! expanded && + { expanded && recommendedChannels.map( ( el, idx ) => ( diff --git a/plugins/woocommerce-admin/client/marketing/overview-multichannel/MarketingOverviewMultichannel.tsx b/plugins/woocommerce-admin/client/marketing/overview-multichannel/MarketingOverviewMultichannel.tsx index 3f202877447..4e7a817e8dd 100644 --- a/plugins/woocommerce-admin/client/marketing/overview-multichannel/MarketingOverviewMultichannel.tsx +++ b/plugins/woocommerce-admin/client/marketing/overview-multichannel/MarketingOverviewMultichannel.tsx @@ -61,14 +61,21 @@ export const MarketingOverviewMultichannel: React.FC = () => { return ; } - const shouldShowCampaigns = !! ( + const showCampaigns = !! ( dataRegistered?.length && ( isIntroductionBannerDismissed || metaCampaigns?.total ) ); - const shouldShowExtensions = + const showChannels = !! ( + dataRegistered && + dataRecommended && + ( dataRegistered.length || dataRecommended.length ) + ); + + const showExtensions = !! ( getAdminSetting( 'allowMarketplaceSuggestions', false ) && - currentUserCan( 'install_plugins' ); + currentUserCan( 'install_plugins' ) + ); const onInstalledAndActivated = ( pluginSlug: string ) => { refetchCampaignTypes(); @@ -86,18 +93,17 @@ export const MarketingOverviewMultichannel: React.FC = () => { } } /> ) } - { shouldShowCampaigns && } - { !! ( dataRegistered && dataRecommended ) && - !! ( dataRegistered.length || dataRecommended.length ) && ( - - ) } + { showCampaigns && } + { showChannels && ( + + ) } - { !! shouldShowExtensions && } + { showExtensions && }
); From 4245ec17b9463c226213b03421c7631b7d2b2455 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Mon, 27 Mar 2023 20:44:38 +0530 Subject: [PATCH 054/466] Yoda conditions, I have used. --- .../src/Database/Migrations/CustomOrderTable/CLIRunner.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php index 4e3cf52c14c..005220121d9 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php @@ -324,7 +324,7 @@ class CLIRunner { $processed = 0; $order_id_start = (int) $assoc_args['start-from']; $order_id_end = (int) $assoc_args['end-at']; - $order_id_end = $order_id_end === - 1 ? PHP_INT_MAX : $order_id_end; + $order_id_end = -1 === $order_id_end ? PHP_INT_MAX : $order_id_end; $order_count = $this->get_verify_order_count( $order_id_start, $order_id_end ); $batch_size = ( (int) $assoc_args['batch-size'] ) === 0 ? 500 : (int) $assoc_args['batch-size']; From 69a00e8eaadcba91c549ed4797327ab1793fda20 Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Mon, 27 Mar 2023 23:29:59 +0800 Subject: [PATCH 055/466] Add changelog. --- .../changelog/feature-marketing-boolean-expression | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/feature-marketing-boolean-expression diff --git a/plugins/woocommerce/changelog/feature-marketing-boolean-expression b/plugins/woocommerce/changelog/feature-marketing-boolean-expression new file mode 100644 index 00000000000..88fadf329b4 --- /dev/null +++ b/plugins/woocommerce/changelog/feature-marketing-boolean-expression @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Simplify boolean expression before && in Marketing page. From a5b104eaed1348c7ef795ccf32897d01e7ce98e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maikel=20David=20P=C3=A9rez=20G=C3=B3mez?= Date: Mon, 27 Mar 2023 12:52:03 -0300 Subject: [PATCH 056/466] Add icon support to product section block (#37340) * Add block icon component * Use the block icon component within the section block * Add changelog file * Fix rebase conflicts * Remove icon configuration from the client side * Change the BlockIcon component to get the icon from the attributes first and then from the metadata * Expose the BlockIcon to be used outside of the package * Add the BlockIcon documentation * Configure the block to support the new icon via attributes * Set the icon to the block section in the server template definition * Revert back index.tsx -> index.ts in the section block * Fix php linter errors * Add changelog file * Fix php linter error * Return null instead of a Fragment --- .../js/product-editor/changelog/add-37264 | 4 ++ .../src/components/block-icon/README.md | 72 +++++++++++++++++++ .../src/components/block-icon/block-icon.tsx | 67 +++++++++++++++++ .../src/components/block-icon/index.ts | 2 + .../src/components/block-icon/types.ts | 3 + .../js/product-editor/src/components/index.ts | 4 ++ .../src/components/section/block.json | 3 + .../src/components/section/edit.tsx | 16 ++++- .../src/components/section/index.ts | 27 ++++--- .../src/components/section/style.scss | 12 ++++ .../src/components/section/types.ts | 9 +++ plugins/woocommerce/changelog/add-37264 | 4 ++ .../includes/class-wc-post-types.php | 6 ++ 13 files changed, 218 insertions(+), 11 deletions(-) create mode 100644 packages/js/product-editor/changelog/add-37264 create mode 100644 packages/js/product-editor/src/components/block-icon/README.md create mode 100644 packages/js/product-editor/src/components/block-icon/block-icon.tsx create mode 100644 packages/js/product-editor/src/components/block-icon/index.ts create mode 100644 packages/js/product-editor/src/components/block-icon/types.ts create mode 100644 packages/js/product-editor/src/components/section/types.ts create mode 100644 plugins/woocommerce/changelog/add-37264 diff --git a/packages/js/product-editor/changelog/add-37264 b/packages/js/product-editor/changelog/add-37264 new file mode 100644 index 00000000000..cd3fe0ca148 --- /dev/null +++ b/packages/js/product-editor/changelog/add-37264 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +The BlockIcon component diff --git a/packages/js/product-editor/src/components/block-icon/README.md b/packages/js/product-editor/src/components/block-icon/README.md new file mode 100644 index 00000000000..c224a38b0ba --- /dev/null +++ b/packages/js/product-editor/src/components/block-icon/README.md @@ -0,0 +1,72 @@ +# BlockIcon + +This component uses the icon defined as a block attribute or metadata and renders it. + +It looks first within the block's `attributes` and if there is no icon defined there, then looks at the block's `metadata`. + +## Usage + +### Icon configuration + +1. As a block attribute + + In the block configuration file `./block.json` + + ```json + "attributes": { + "icon": { + "type": "object" + } + } + ``` + + In the server during the template configuration + + ```php + array( + 'woocommerce/product-section', // Block name + array( + // Block attributes + 'icon' => array( + // It's possible to pass a valid html string + 'src' => '', + + // Or an absolute url + 'src' => 'https://...', + 'alt' => 'The alt name for the icon', + + // Or a Dashicon icon-key + 'src' => 'default-block', + ), + ), + array( + // Inner blocks + ), + ), + ``` + +2. As part of the block's metadata + + See [the official blocks icon documentation](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#icon). + +### Rendering the Icon + +```javascript +import { __experimentalBlockIcon as BlockIcon } from '@woocommerce/product-editor'; + +export function BlockEdit( { clientId } ) { + const blockProps = useBlockProps(); + + return ( +
+

+ + + { title } +

+ + +
+ ); +} +``` diff --git a/packages/js/product-editor/src/components/block-icon/block-icon.tsx b/packages/js/product-editor/src/components/block-icon/block-icon.tsx new file mode 100644 index 00000000000..fda5b44a7b9 --- /dev/null +++ b/packages/js/product-editor/src/components/block-icon/block-icon.tsx @@ -0,0 +1,67 @@ +/** + * External dependencies + */ +import { BlockIcon as BaseBlockIcon } from '@wordpress/block-editor'; +import { Block } from '@wordpress/blocks'; +import { useSelect } from '@wordpress/data'; +import { createElement, RawHTML } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { BlockIconProps } from './types'; + +export function BlockIcon( { clientId }: BlockIconProps ) { + const icon = useSelect( + ( select ) => { + // Try to get the icon from the block's attributes + const { getBlockAttributes, getBlockName } = + select( 'core/block-editor' ); + const attributes = getBlockAttributes( clientId ); + if ( attributes?.icon ) { + return attributes.icon; + } + + // If there is no icon defined in attributes + // Then try to get icon from block's metadata + const { getBlockType } = select( 'core/blocks' ); + const blockName = getBlockName( clientId ); + const block = getBlockType< Block >( blockName ); + return block?.icon; + }, + [ clientId ] + ); + + if ( ! icon ) { + return null; + } + + if ( typeof icon === 'object' ) { + const { src, ...iconProps } = icon; + + if ( /^<(.)+>$/.test( src ) ) { + const iconComponent = ( + + ); + return ; + } + + if ( /^https?:\/\/(.)+/.test( src ) ) { + const iconImage = ( + + ); + return ; + } + } + + return ; +} diff --git a/packages/js/product-editor/src/components/block-icon/index.ts b/packages/js/product-editor/src/components/block-icon/index.ts new file mode 100644 index 00000000000..9e20e38d3cf --- /dev/null +++ b/packages/js/product-editor/src/components/block-icon/index.ts @@ -0,0 +1,2 @@ +export * from './block-icon'; +export * from './types'; diff --git a/packages/js/product-editor/src/components/block-icon/types.ts b/packages/js/product-editor/src/components/block-icon/types.ts new file mode 100644 index 00000000000..b1e47017faf --- /dev/null +++ b/packages/js/product-editor/src/components/block-icon/types.ts @@ -0,0 +1,3 @@ +export type BlockIconProps = { + clientId: string; +}; diff --git a/packages/js/product-editor/src/components/index.ts b/packages/js/product-editor/src/components/index.ts index 8a1005c6a43..23075d35584 100644 --- a/packages/js/product-editor/src/components/index.ts +++ b/packages/js/product-editor/src/components/index.ts @@ -15,3 +15,7 @@ export { Editor as __experimentalEditor, ProductEditorSettings, } from './editor'; +export { + BlockIcon as __experimentalBlockIcon, + BlockIconProps, +} from './block-icon'; diff --git a/packages/js/product-editor/src/components/section/block.json b/packages/js/product-editor/src/components/section/block.json index e1d8a35c111..89f8811fb0c 100644 --- a/packages/js/product-editor/src/components/section/block.json +++ b/packages/js/product-editor/src/components/section/block.json @@ -13,6 +13,9 @@ }, "description": { "type": "string" + }, + "icon": { + "type": "object" } }, "supports": { diff --git a/packages/js/product-editor/src/components/section/edit.tsx b/packages/js/product-editor/src/components/section/edit.tsx index 7a46d9ae566..369055d4a0d 100644 --- a/packages/js/product-editor/src/components/section/edit.tsx +++ b/packages/js/product-editor/src/components/section/edit.tsx @@ -3,16 +3,26 @@ */ import { createElement } from '@wordpress/element'; import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; -import type { BlockAttributes } from '@wordpress/blocks'; +import type { BlockEditProps } from '@wordpress/blocks'; -export function Edit( { attributes }: { attributes: BlockAttributes } ) { +/** + * Internal dependencies + */ +import { SectionBlockAttributes } from './types'; +import { BlockIcon } from '../block-icon'; + +export function Edit( { + attributes, + clientId, +}: BlockEditProps< SectionBlockAttributes > ) { const blockProps = useBlockProps(); const { description, title } = attributes; return (

- { title } + + { title }

{ description } diff --git a/packages/js/product-editor/src/components/section/index.ts b/packages/js/product-editor/src/components/section/index.ts index 15144b2b28d..70224538e05 100644 --- a/packages/js/product-editor/src/components/section/index.ts +++ b/packages/js/product-editor/src/components/section/index.ts @@ -1,17 +1,28 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { BlockConfiguration } from '@wordpress/blocks'; + /** * Internal dependencies */ -import initBlock from '../../utils/init-block'; -import metadata from './block.json'; +import { initBlock } from '../../utils/init-blocks'; +import blockConfiguration from './block.json'; import { Edit } from './edit'; +import { SectionBlockAttributes } from './types'; -const { name } = metadata; +const { name, ...metadata } = + blockConfiguration as BlockConfiguration< SectionBlockAttributes >; export { metadata, name }; -export const settings = { - example: {}, - edit: Edit, -}; +export const settings: Partial< BlockConfiguration< SectionBlockAttributes > > = + { + example: {}, + edit: Edit, + }; -export const init = () => initBlock( { name, metadata, settings } ); +export function init() { + return initBlock( { name, metadata, settings } ); +} diff --git a/packages/js/product-editor/src/components/section/style.scss b/packages/js/product-editor/src/components/section/style.scss index d3341c04f49..137bead30c2 100644 --- a/packages/js/product-editor/src/components/section/style.scss +++ b/packages/js/product-editor/src/components/section/style.scss @@ -7,6 +7,18 @@ font-size: 24px; font-weight: 500; color: $gray-900; + display: inline-flex; + align-items: center; + + .block-editor-block-icon { + margin-right: 14px; + + > div { + display: flex; + align-items: center; + justify-content: center; + } + } } &__description { diff --git a/packages/js/product-editor/src/components/section/types.ts b/packages/js/product-editor/src/components/section/types.ts new file mode 100644 index 00000000000..017ae754a7d --- /dev/null +++ b/packages/js/product-editor/src/components/section/types.ts @@ -0,0 +1,9 @@ +/** + * External dependencies + */ +import { BlockAttributes } from '@wordpress/blocks'; + +export interface SectionBlockAttributes extends BlockAttributes { + title: string; + description: string; +} diff --git a/plugins/woocommerce/changelog/add-37264 b/plugins/woocommerce/changelog/add-37264 new file mode 100644 index 00000000000..e340926e2e7 --- /dev/null +++ b/plugins/woocommerce/changelog/add-37264 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add image configuration to the product block template diff --git a/plugins/woocommerce/includes/class-wc-post-types.php b/plugins/woocommerce/includes/class-wc-post-types.php index 5fcb68552a5..3d630729817 100644 --- a/plugins/woocommerce/includes/class-wc-post-types.php +++ b/plugins/woocommerce/includes/class-wc-post-types.php @@ -379,6 +379,9 @@ class WC_Post_Types { array( 'title' => __( 'Basic details', 'woocommerce' ), 'description' => __( 'This info will be displayed on the product page, category pages, social media, and search results.', 'woocommerce' ), + 'icon' => array( + 'src' => '', + ), ), array( array( @@ -456,6 +459,9 @@ class WC_Post_Types { 'woocommerce/product-section', array( 'title' => __( 'Shipping section', 'woocommerce' ), + 'icon' => array( + 'src' => '', + ), ), array( array( From 7a68b6f16f7c5dc237be80e18fcbb7505acb88bc Mon Sep 17 00:00:00 2001 From: Leif Singer Date: Mon, 27 Mar 2023 18:13:26 +0200 Subject: [PATCH 057/466] Delete changelog files based on PR #37434 (#37449) --- .../changelog/dev-update-required-wordpress-version | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 plugins/woocommerce/changelog/dev-update-required-wordpress-version diff --git a/plugins/woocommerce/changelog/dev-update-required-wordpress-version b/plugins/woocommerce/changelog/dev-update-required-wordpress-version deleted file mode 100644 index 46d408dca99..00000000000 --- a/plugins/woocommerce/changelog/dev-update-required-wordpress-version +++ /dev/null @@ -1,4 +0,0 @@ -Significance: major -Type: dev - -Update tested and required WordPress versions according to L-2 policy. From 82a172cd066423601841cda945bbe48535552334 Mon Sep 17 00:00:00 2001 From: Leif Singer Date: Mon, 27 Mar 2023 20:57:05 +0200 Subject: [PATCH 058/466] Delete changelog files based on PR 37323 (#37456) --- plugins/woocommerce/changelog/e2e-fix-failing-malta-test | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 plugins/woocommerce/changelog/e2e-fix-failing-malta-test diff --git a/plugins/woocommerce/changelog/e2e-fix-failing-malta-test b/plugins/woocommerce/changelog/e2e-fix-failing-malta-test deleted file mode 100644 index cc9f444a669..00000000000 --- a/plugins/woocommerce/changelog/e2e-fix-failing-malta-test +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Update OBW end to end test as WC Pay supports Malta now From 1bcc039bf51e6615b48b1befea7ab55442199a49 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:12:10 +0200 Subject: [PATCH 059/466] Delete changelog files based on PR 37313 (#37358) Delete changelog files for 37313 Co-authored-by: WooCommerce Bot --- plugins/woocommerce/changelog/fix-order-cache-duplicate | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 plugins/woocommerce/changelog/fix-order-cache-duplicate diff --git a/plugins/woocommerce/changelog/fix-order-cache-duplicate b/plugins/woocommerce/changelog/fix-order-cache-duplicate deleted file mode 100644 index 1fb3ace0c40..00000000000 --- a/plugins/woocommerce/changelog/fix-order-cache-duplicate +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Overwrite clone method to prevent duplicate datq when saving a clone. From bfe49d9d834ba577ebda4ac50d0d871ec00994d1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:39:19 +0200 Subject: [PATCH 060/466] Delete changelog files based on PR 37397 (#37423) Delete changelog files for 37397 Co-authored-by: WooCommerce Bot --- plugins/woocommerce/changelog/fix-37389-error-option-not-set | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 plugins/woocommerce/changelog/fix-37389-error-option-not-set diff --git a/plugins/woocommerce/changelog/fix-37389-error-option-not-set b/plugins/woocommerce/changelog/fix-37389-error-option-not-set deleted file mode 100644 index 00bec2f5166..00000000000 --- a/plugins/woocommerce/changelog/fix-37389-error-option-not-set +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Add default value when calling get_option for woocommerce_task_list_tracked_completed_tasks. From 4b64923747e071b00d7fb6ddf1fccaaf4b4010ab Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 27 Mar 2023 22:19:24 +0200 Subject: [PATCH 061/466] Synchronized SSR from template to REST API (#37425) * Synchronized SSR from template to REST API. * Changelog. * PHPCS * More PHPCS. * PHPCS fix * Fixed unit tests. --- .../changelog/fix-sync-ssr-REST-and-UI | 4 + .../views/html-admin-page-status-report.php | 22 +++-- ...ss-wc-rest-system-status-v2-controller.php | 97 ++++++++++++++----- .../rest-api/Tests/Version2/system-status.php | 2 +- .../rest-api/Tests/Version3/system-status.php | 2 +- 5 files changed, 95 insertions(+), 32 deletions(-) create mode 100644 plugins/woocommerce/changelog/fix-sync-ssr-REST-and-UI diff --git a/plugins/woocommerce/changelog/fix-sync-ssr-REST-and-UI b/plugins/woocommerce/changelog/fix-sync-ssr-REST-and-UI new file mode 100644 index 00000000000..6891ea83cbe --- /dev/null +++ b/plugins/woocommerce/changelog/fix-sync-ssr-REST-and-UI @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Synchronized SSR from template to REST API. diff --git a/plugins/woocommerce/includes/admin/views/html-admin-page-status-report.php b/plugins/woocommerce/includes/admin/views/html-admin-page-status-report.php index 1d9b77524dc..7c0f04f3b97 100644 --- a/plugins/woocommerce/includes/admin/views/html-admin-page-status-report.php +++ b/plugins/woocommerce/includes/admin/views/html-admin-page-status-report.php @@ -6,8 +6,6 @@ */ use Automattic\Jetpack\Constants; -use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Register as Download_Directories; -use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer as Order_DataSynchronizer; defined( 'ABSPATH' ) || exit; @@ -761,20 +759,30 @@ if ( 0 < count( $dropins_mu_plugins['mu_plugins'] ) ) : : - get( Download_Directories::class )->get_mode() === Download_Directories::MODE_ENABLED ? '' : ''; ?> + ' : ''; ?> + + + + + + ' : ''; ?> + + + + + ' : ''; ?> - get_current_class_name() ); ?> + - get( Automattic\WooCommerce\Internal\Features\FeaturesController::class )->feature_is_enabled( 'custom_order_tables' ) ) : ?> - get( Order_DataSynchronizer::class )->data_sync_is_enabled() ? '' : ''; ?> + ' : ''; ?> - + diff --git a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php index 18c6f8df46b..6b6192bd1ba 100644 --- a/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php +++ b/plugins/woocommerce/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-v2-controller.php @@ -11,6 +11,9 @@ defined( 'ABSPATH' ) || exit; use Automattic\WooCommerce\Internal\WCCom\ConnectionHelper; +use Automattic\WooCommerce\Internal\ProductDownloads\ApprovedDirectories\Register as Download_Directories; +use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer as Order_DataSynchronizer; +use Automattic\WooCommerce\Utilities\OrderUtil; /** * System status controller class. * @@ -475,61 +478,61 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller { 'context' => array( 'view' ), 'readonly' => true, 'properties' => array( - 'api_enabled' => array( + 'api_enabled' => array( 'description' => __( 'REST API enabled?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), - 'force_ssl' => array( + 'force_ssl' => array( 'description' => __( 'SSL forced?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), - 'currency' => array( + 'currency' => array( 'description' => __( 'Currency.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), - 'currency_symbol' => array( + 'currency_symbol' => array( 'description' => __( 'Currency symbol.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), - 'currency_position' => array( + 'currency_position' => array( 'description' => __( 'Currency position.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), - 'thousand_separator' => array( + 'thousand_separator' => array( 'description' => __( 'Thousand separator.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), - 'decimal_separator' => array( + 'decimal_separator' => array( 'description' => __( 'Decimal separator.', 'woocommerce' ), 'type' => 'string', 'context' => array( 'view' ), 'readonly' => true, ), - 'number_of_decimals' => array( + 'number_of_decimals' => array( 'description' => __( 'Number of decimals.', 'woocommerce' ), 'type' => 'integer', 'context' => array( 'view' ), 'readonly' => true, ), - 'geolocation_enabled' => array( + 'geolocation_enabled' => array( 'description' => __( 'Geolocation enabled?', 'woocommerce' ), 'type' => 'boolean', 'context' => array( 'view' ), 'readonly' => true, ), - 'taxonomies' => array( + 'taxonomies' => array( 'description' => __( 'Taxonomy terms for product/order statuses.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view' ), @@ -538,7 +541,7 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller { 'type' => 'string', ), ), - 'product_visibility_terms' => array( + 'product_visibility_terms' => array( 'description' => __( 'Terms in the product visibility taxonomy.', 'woocommerce' ), 'type' => 'array', 'context' => array( 'view' ), @@ -547,6 +550,42 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller { 'type' => 'string', ), ), + 'wccom_connected' => array( + 'description' => __( 'Is store connected to WooCommerce.com?', 'woocommerce' ), + 'type' => 'string', + 'context' => array( 'view' ), + 'readonly' => true, + ), + 'enforce_approved_download_dirs' => array( + 'description' => __( 'Enforce approved download directories?', 'woocommerce' ), + 'type' => 'boolean', + 'context' => array( 'view' ), + 'readonly' => true, + ), + 'HPOS_feature_screen_enabled' => array( + 'description' => __( 'Is HPOS feature screen enabled?', 'woocommerce' ), + 'type' => 'boolean', + 'context' => array( 'view' ), + 'readonly' => true, + ), + 'HPOS_enabled' => array( + 'description' => __( 'Is HPOS enabled?', 'woocommerce' ), + 'type' => 'boolean', + 'context' => array( 'view' ), + 'readonly' => true, + ), + 'order_datastore' => array( + 'description' => __( 'Order datastore.', 'woocommerce' ), + 'type' => 'string', + 'context' => array( 'view' ), + 'readonly' => true, + ), + 'HPOS_sync_enabled' => array( + 'description' => __( 'Is HPOS sync enabled?', 'woocommerce' ), + 'type' => 'boolean', + 'context' => array( 'view' ), + 'readonly' => true, + ), ), ), 'security' => array( @@ -1238,18 +1277,30 @@ class WC_REST_System_Status_V2_Controller extends WC_REST_Controller { // Return array of useful settings for debugging. return array( - 'api_enabled' => 'yes' === get_option( 'woocommerce_api_enabled' ), - 'force_ssl' => 'yes' === get_option( 'woocommerce_force_ssl_checkout' ), - 'currency' => get_woocommerce_currency(), - 'currency_symbol' => get_woocommerce_currency_symbol(), - 'currency_position' => get_option( 'woocommerce_currency_pos' ), - 'thousand_separator' => wc_get_price_thousand_separator(), - 'decimal_separator' => wc_get_price_decimal_separator(), - 'number_of_decimals' => wc_get_price_decimals(), - 'geolocation_enabled' => in_array( get_option( 'woocommerce_default_customer_address' ), array( 'geolocation_ajax', 'geolocation' ), true ), - 'taxonomies' => $term_response, - 'product_visibility_terms' => $product_visibility_terms, - 'woocommerce_com_connected' => ConnectionHelper::is_connected() ? 'yes' : 'no', + 'api_enabled' => 'yes' === get_option( 'woocommerce_api_enabled' ), + 'force_ssl' => 'yes' === get_option( 'woocommerce_force_ssl_checkout' ), + 'currency' => get_woocommerce_currency(), + 'currency_symbol' => get_woocommerce_currency_symbol(), + 'currency_position' => get_option( 'woocommerce_currency_pos' ), + 'thousand_separator' => wc_get_price_thousand_separator(), + 'decimal_separator' => wc_get_price_decimal_separator(), + 'number_of_decimals' => wc_get_price_decimals(), + 'geolocation_enabled' => in_array( + get_option( 'woocommerce_default_customer_address' ), + array( + 'geolocation_ajax', + 'geolocation', + ), + true + ), + 'taxonomies' => $term_response, + 'product_visibility_terms' => $product_visibility_terms, + 'woocommerce_com_connected' => ConnectionHelper::is_connected() ? 'yes' : 'no', + 'enforce_approved_download_dirs' => wc_get_container()->get( Download_Directories::class )->get_mode() === Download_Directories::MODE_ENABLED, + 'order_datastore' => WC_Data_Store::load( 'order' )->get_current_class_name(), + 'HPOS_feature_screen_enabled' => wc_get_container()->get( Automattic\WooCommerce\Internal\Features\FeaturesController::class )->feature_is_enabled( 'custom_order_tables' ), + 'HPOS_enabled' => OrderUtil::custom_orders_table_usage_is_enabled(), + 'HPOS_sync_enabled' => wc_get_container()->get( Order_DataSynchronizer::class )->data_sync_is_enabled(), ); } diff --git a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version2/system-status.php b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version2/system-status.php index 4fe9ef49a99..663bc662d48 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version2/system-status.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version2/system-status.php @@ -178,7 +178,7 @@ class WC_Tests_REST_System_Status_V2 extends WC_REST_Unit_Test_Case { $data = $response->get_data(); $settings = (array) $data['settings']; - $this->assertEquals( 12, count( $settings ) ); + $this->assertEquals( 17, count( $settings ) ); $this->assertEquals( ( 'yes' === get_option( 'woocommerce_api_enabled' ) ), $settings['api_enabled'] ); $this->assertEquals( get_woocommerce_currency(), $settings['currency'] ); $this->assertEquals( $term_response, $settings['taxonomies'] ); diff --git a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/system-status.php b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/system-status.php index 4fe8ef29055..53ed089d103 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/system-status.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/system-status.php @@ -201,7 +201,7 @@ class WC_Tests_REST_System_Status extends WC_REST_Unit_Test_Case { $data = $response->get_data(); $settings = (array) $data['settings']; - $this->assertEquals( 12, count( $settings ) ); + $this->assertEquals( 17, count( $settings ) ); $this->assertEquals( ( 'yes' === get_option( 'woocommerce_api_enabled' ) ), $settings['api_enabled'] ); $this->assertEquals( get_woocommerce_currency(), $settings['currency'] ); $this->assertEquals( $term_response, $settings['taxonomies'] ); From 7918d0a3c2ab049d77d6e631e8213c6420447afc Mon Sep 17 00:00:00 2001 From: Sam Seay Date: Tue, 28 Mar 2023 10:58:17 +1300 Subject: [PATCH 062/466] Revert "Replace $.ajax() calls with browser-native window.fetch() calls. (#36275)" This reverts commit 6c053d3eeca2805658f45a15e57ddd96d78bc1ab. --- .../js/frontend/add-to-cart-variation.js | 28 +--- .../client/legacy/js/frontend/add-to-cart.js | 22 +--- .../legacy/js/frontend/cart-fragments.js | 24 +--- .../client/legacy/js/frontend/cart.js | 57 +++------ .../client/legacy/js/frontend/checkout.js | 120 +++++++----------- .../client/legacy/js/frontend/geolocation.js | 12 +- 6 files changed, 73 insertions(+), 190 deletions(-) diff --git a/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js b/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js index d5e6bfa26a6..ba2ba97eb25 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js +++ b/plugins/woocommerce/client/legacy/js/frontend/add-to-cart-variation.js @@ -167,16 +167,16 @@ if ( attributes.count && attributes.count === attributes.chosenCount ) { if ( form.useAjax ) { - if ( form.controller ) { - form.controller.abort(); + if ( form.xhr ) { + form.xhr.abort(); } form.$form.block( { message: null, overlayCSS: { background: '#fff', opacity: 0.6 } } ); currentAttributes.product_id = parseInt( form.$form.data( 'product_id' ), 10 ); currentAttributes.custom_data = form.$form.data( 'custom_data' ); - const options = { + form.xhr = $.ajax( { url: wc_add_to_cart_variation_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_variation' ), type: 'POST', - data: $.param( currentAttributes ), + data: currentAttributes, success: function( variation ) { if ( variation ) { form.$form.trigger( 'found_variation', [ variation ] ); @@ -199,25 +199,7 @@ complete: function() { form.$form.unblock(); } - }; - - const controller = new AbortController(); - form.controller = controller; - - window.fetch( options.url, { - method: options.type, - headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, - body: options.data, - signal: controller.signal - } ) - .then( response => { - if ( !response.ok ) { - throw new Error( response.statusText ); - } - return response.json(); - }) - .then( options.success ) - .finally( () => options.complete() ); + } ); } else { form.$form.trigger( 'update_variation_values' ); diff --git a/plugins/woocommerce/client/legacy/js/frontend/add-to-cart.js b/plugins/woocommerce/client/legacy/js/frontend/add-to-cart.js index dac6676e560..e27baf072a4 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/add-to-cart.js +++ b/plugins/woocommerce/client/legacy/js/frontend/add-to-cart.js @@ -51,21 +51,7 @@ jQuery( function( $ ) { } }; - const options = this.requests[0]; - window.fetch( options.url, { - method: options.type, - headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, - body: options.data - } ) - .then( response => { - if ( !response.ok ) { - throw new Error( response.statusText ); - } - return response.json(); - } ) - .then( options.success ) - .catch( error => options.error && options.error() ) - .finally( () => options.complete && options.complete() ); + $.ajax( this.requests[0] ); }; /** @@ -109,7 +95,7 @@ jQuery( function( $ ) { e.data.addToCartHandler.addRequest({ type: 'POST', url: wc_add_to_cart_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'add_to_cart' ), - data: $.param( data ), + data: data, success: function( response ) { if ( ! response ) { return; @@ -153,9 +139,9 @@ jQuery( function( $ ) { e.data.addToCartHandler.addRequest({ type: 'POST', url: wc_add_to_cart_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'remove_from_cart' ), - data: new URLSearchParams( { + data: { cart_item_key : $thisbutton.data( 'cart_item_key' ) - } ).toString(), + }, success: function( response ) { if ( ! response || ! response.fragments ) { window.location = $thisbutton.attr( 'href' ); diff --git a/plugins/woocommerce/client/legacy/js/frontend/cart-fragments.js b/plugins/woocommerce/client/legacy/js/frontend/cart-fragments.js index b57b2618d84..c5eef7b4c72 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/cart-fragments.js +++ b/plugins/woocommerce/client/legacy/js/frontend/cart-fragments.js @@ -38,9 +38,9 @@ jQuery( function( $ ) { var $fragment_refresh = { url: wc_cart_fragments_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_refreshed_fragments' ), type: 'POST', - data: new URLSearchParams( { + data: { time: new Date().getTime() - } ).toString(), + }, timeout: wc_cart_fragments_params.request_timeout, success: function( data ) { if ( data && data.fragments ) { @@ -68,25 +68,7 @@ jQuery( function( $ ) { /* Named callback for refreshing cart fragment */ function refresh_cart_fragment() { - const controller = new AbortController(); - const timeoutId = setTimeout( () => controller.abort(), $fragment_refresh.timeout ); - - window.fetch( $fragment_refresh.url, { - method: $fragment_refresh.type, - headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, - body: $fragment_refresh.data, - signal: controller.signal - } ) - .then( response => { - clearTimeout( timeoutId ); - - if ( !response.ok ) { - throw new Error( response.statusText ); - } - return response.json(); - } ) - .then( $fragment_refresh.success ) - .catch( error => $fragment_refresh.error() ); + $.ajax( $fragment_refresh ); } /* Cart Handling */ diff --git a/plugins/woocommerce/client/legacy/js/frontend/cart.js b/plugins/woocommerce/client/legacy/js/frontend/cart.js index 62524a6bd1e..cdee33f5d79 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/cart.js +++ b/plugins/woocommerce/client/legacy/js/frontend/cart.js @@ -8,27 +8,6 @@ jQuery( function( $ ) { // Utility functions for the file. - /** - * Perform an AJAX request that expects an HTML response. - * - * @param {Object} options - */ - const ajax = options => { - window.fetch( options.url, { - method: options.type || 'GET', - headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, - body: options.data - } ) - .then( response => { - if ( !response.ok ) { - throw new Error( response.statusText ); - } - return response.text(); - } ) - .then( options.success ) - .finally( () => options.complete() ); - }; - /** * Gets a url for a given AJAX endpoint. * @@ -243,17 +222,13 @@ jQuery( function( $ ) { var data = { security: wc_cart_params.update_shipping_method_nonce, + shipping_method: shipping_methods }; - // Flatten shipping_methods for use in URLSearchParams() - for ( var k in shipping_methods ) { - data[ 'shipping_method[' + k + ']' ] = shipping_methods[ k ]; - } - - ajax( { + $.ajax( { type: 'post', url: get_url( 'update_shipping_method' ), - data: new URLSearchParams( data ).toString(), + data: data, dataType: 'html', success: function( response ) { update_cart_totals_div( response ); @@ -285,10 +260,10 @@ jQuery( function( $ ) { .appendTo( $form ); // Make call to actual form post URL. - ajax( { + $.ajax( { type: $form.attr( 'method' ), url: $form.attr( 'action' ), - data: new URLSearchParams( new FormData( $form[0] ) ).toString(), + data: $form.serialize(), dataType: 'html', success: function( response ) { update_wc_div( response ); @@ -372,10 +347,10 @@ jQuery( function( $ ) { block( $( 'div.cart_totals' ) ); // Make call to actual form post URL. - ajax( { + $.ajax( { type: $form.attr( 'method' ), url: $form.attr( 'action' ), - data: new URLSearchParams( new FormData( $form[0] ) ).toString(), + data: $form.serialize(), dataType: 'html', success: function( response ) { update_wc_div( response, preserve_notices ); @@ -394,7 +369,7 @@ jQuery( function( $ ) { update_cart_totals: function() { block( $( 'div.cart_totals' ) ); - ajax( { + $.ajax( { url: get_url( 'get_cart_totals' ), dataType: 'html', success: function( response ) { @@ -496,10 +471,10 @@ jQuery( function( $ ) { coupon_code: coupon_code }; - ajax( { + $.ajax( { type: 'POST', url: get_url( 'apply_coupon' ), - data: new URLSearchParams( data ).toString(), + data: data, dataType: 'html', success: function( response ) { $( '.woocommerce-error, .woocommerce-message, .woocommerce-info' ).remove(); @@ -533,10 +508,10 @@ jQuery( function( $ ) { coupon: coupon }; - ajax( { + $.ajax( { type: 'POST', url: get_url( 'remove_coupon' ), - data: new URLSearchParams( data ).toString(), + data: data, dataType: 'html', success: function( response ) { $( '.woocommerce-error, .woocommerce-message, .woocommerce-info' ).remove(); @@ -566,10 +541,10 @@ jQuery( function( $ ) { .appendTo( $form ); // Make call to actual form post URL. - ajax( { + $.ajax( { type: $form.attr( 'method' ), url: $form.attr( 'action' ), - data: new URLSearchParams( new FormData( $form[0] ) ).toString(), + data: $form.serialize(), dataType: 'html', success: function( response ) { update_wc_div( response ); @@ -596,7 +571,7 @@ jQuery( function( $ ) { block( $form ); block( $( 'div.cart_totals' ) ); - ajax( { + $.ajax( { type: 'GET', url: $a.attr( 'href' ), dataType: 'html', @@ -625,7 +600,7 @@ jQuery( function( $ ) { block( $form ); block( $( 'div.cart_totals' ) ); - ajax( { + $.ajax( { type: 'GET', url: $a.attr( 'href' ), dataType: 'html', diff --git a/plugins/woocommerce/client/legacy/js/frontend/checkout.js b/plugins/woocommerce/client/legacy/js/frontend/checkout.js index ff9044164c2..91a33b69329 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/checkout.js +++ b/plugins/woocommerce/client/legacy/js/frontend/checkout.js @@ -8,38 +8,6 @@ jQuery( function( $ ) { $.blockUI.defaults.overlayCSS.cursor = 'default'; - const ajax = options => { - const controller = new AbortController(); - - window.fetch( options.url, { - method: options.type, - headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, - body: options.data, - signal: controller.signal - } ) - .then( response => { - response.text().then( text => { - if ( !response.ok ) { - const error = new Error( response.statusText ); - error.responseText = text; // Needed for when wc_checkout_params.debug_mode is enabled - throw error; - } - - if ( options.dataType === 'html' ) { - options.success( text ); - return; - } - - const json = JSON.parse( ajax.dataFilter( text, 'json' ) ); - options.success( json ); - } ); - } ) - .catch( error => options.error && options.error( error.responseText, 'error', error.message ) ); - - return controller; - }; - ajax.dataFilter = ( raw_response, dataType ) => raw_response; - var wc_checkout_form = { updateTimer: false, dirtyInput: false, @@ -288,12 +256,11 @@ jQuery( function( $ ) { wc_checkout_form.updateTimer = setTimeout( wc_checkout_form.update_checkout_action, '5', args ); }, update_checkout_action: function( args ) { - if ( wc_checkout_form.controller ) { - wc_checkout_form.controller.abort(); + if ( wc_checkout_form.xhr ) { + wc_checkout_form.xhr.abort(); } - var $form = $( 'form.checkout' ); - if ( $form.length === 0 ) { + if ( $( 'form.checkout' ).length === 0 ) { return; } @@ -349,7 +316,7 @@ jQuery( function( $ ) { s_address : s_address, s_address_2 : s_address_2, has_full_address: has_full_address, - post_data : new URLSearchParams( new FormData( $form[0] ) ).toString() + post_data : $( 'form.checkout' ).serialize() }; if ( false !== args.update_shipping_method ) { @@ -360,10 +327,7 @@ jQuery( function( $ ) { shipping_methods[ $( this ).data( 'index' ) ] = $( this ).val(); } ); - // Flatten shipping_methods for use in URLSearchParams() - for ( var k in shipping_methods ) { - data[ 'shipping_method[' + k + ']' ] = shipping_methods[ k ]; - } + data.shipping_method = shipping_methods; } $( '.woocommerce-checkout-payment, .woocommerce-checkout-review-order-table' ).block({ @@ -374,10 +338,10 @@ jQuery( function( $ ) { } }); - wc_checkout_form.controller = ajax({ + wc_checkout_form.xhr = $.ajax({ type: 'POST', url: wc_checkout_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'update_order_review' ), - data: new URLSearchParams( data ).toString(), + data: data, success: function( data ) { // Reload the page if requested @@ -440,6 +404,8 @@ jQuery( function( $ ) { // Check for error if ( data && 'failure' === data.result ) { + var $form = $( 'form.checkout' ); + // Remove notices from all sources $( '.woocommerce-error, .woocommerce-message' ).remove(); @@ -520,37 +486,39 @@ jQuery( function( $ ) { // Attach event to block reloading the page when the form has been submitted wc_checkout_form.attachUnloadEventsOnSubmit(); - // ajax.dataFilter affects all ajax() calls, but we use it to ensure JSON is valid once returned. - ajax.dataFilter = function( raw_response, dataType ) { - // We only want to work with JSON - if ( 'json' !== dataType ) { - return raw_response; - } - - if ( wc_checkout_form.is_valid_json( raw_response ) ) { - return raw_response; - } else { - // Attempt to fix the malformed JSON - var maybe_valid_json = raw_response.match( /{"result.*}/ ); - - if ( null === maybe_valid_json ) { - console.log( 'Unable to fix malformed JSON' ); - } else if ( wc_checkout_form.is_valid_json( maybe_valid_json[0] ) ) { - console.log( 'Fixed malformed JSON. Original:' ); - console.log( raw_response ); - raw_response = maybe_valid_json[0]; - } else { - console.log( 'Unable to fix malformed JSON' ); + // ajaxSetup is global, but we use it to ensure JSON is valid once returned. + $.ajaxSetup( { + dataFilter: function( raw_response, dataType ) { + // We only want to work with JSON + if ( 'json' !== dataType ) { + return raw_response; } + + if ( wc_checkout_form.is_valid_json( raw_response ) ) { + return raw_response; + } else { + // Attempt to fix the malformed JSON + var maybe_valid_json = raw_response.match( /{"result.*}/ ); + + if ( null === maybe_valid_json ) { + console.log( 'Unable to fix malformed JSON' ); + } else if ( wc_checkout_form.is_valid_json( maybe_valid_json[0] ) ) { + console.log( 'Fixed malformed JSON. Original:' ); + console.log( raw_response ); + raw_response = maybe_valid_json[0]; + } else { + console.log( 'Unable to fix malformed JSON' ); + } + } + + return raw_response; } + } ); - return raw_response; - }; - - ajax({ + $.ajax({ type: 'POST', url: wc_checkout_params.checkout_url, - data: new URLSearchParams( new FormData( $form[0] ) ).toString(), + data: $form.serialize(), dataType: 'json', success: function( result ) { // Detach the unload handler that prevents a reload / redirect @@ -588,7 +556,7 @@ jQuery( function( $ ) { } } }, - error: function( responseText, textStatus, errorThrown ) { + error: function( jqXHR, textStatus, errorThrown ) { // Detach the unload handler that prevents a reload / redirect wc_checkout_form.detachUnloadEventsOnSubmit(); @@ -653,10 +621,10 @@ jQuery( function( $ ) { coupon_code: $form.find( 'input[name="coupon_code"]' ).val() }; - ajax({ + $.ajax({ type: 'POST', url: wc_checkout_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'apply_coupon' ), - data: new URLSearchParams( data ).toString(), + data: data, success: function( code ) { $( '.woocommerce-error, .woocommerce-message' ).remove(); $form.removeClass( 'processing' ).unblock(); @@ -693,10 +661,10 @@ jQuery( function( $ ) { coupon: coupon }; - ajax({ + $.ajax({ type: 'POST', url: wc_checkout_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'remove_coupon' ), - data: new URLSearchParams( data ).toString(), + data: data, success: function( code ) { $( '.woocommerce-error, .woocommerce-message' ).remove(); container.removeClass( 'processing' ).unblock(); @@ -711,10 +679,10 @@ jQuery( function( $ ) { $( 'form.checkout_coupon' ).find( 'input[name="coupon_code"]' ).val( '' ); } }, - error: function ( responseText ) { + error: function ( jqXHR ) { if ( wc_checkout_params.debug_mode ) { /* jshint devel: true */ - console.log( responseText ); + console.log( jqXHR.responseText ); } }, dataType: 'html' diff --git a/plugins/woocommerce/client/legacy/js/frontend/geolocation.js b/plugins/woocommerce/client/legacy/js/frontend/geolocation.js index 29f8435f9df..e7029a3539d 100644 --- a/plugins/woocommerce/client/legacy/js/frontend/geolocation.js +++ b/plugins/woocommerce/client/legacy/js/frontend/geolocation.js @@ -127,17 +127,7 @@ jQuery( function( $ ) { // Get the current geo hash. If it doesn't exist, or if it doesn't match the current // page URL, perform a geolocation request. if ( ! get_geo_hash() || needs_refresh() ) { - window.fetch( $geolocate_customer.url, { - method: $geolocate_customer.type, - headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' } - } ) - .then( response => { - if ( !response.ok ) { - throw new Error( response.statusText ); - } - return response.json(); - } ) - .then( $geolocate_customer.success ); + $.ajax( $geolocate_customer ); } // Page updates. From 6e8f08043822364f3db850c64a04325b00b97583 Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 27 Mar 2023 11:10:56 -0700 Subject: [PATCH 063/466] Restore reviews (comments) to the product editor. --- .../ReviewsCommentsOverrides.php | 7 ++++ .../ReviewsCommentsOverridesTest.php | 34 ++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/plugins/woocommerce/src/Internal/Admin/ProductReviews/ReviewsCommentsOverrides.php b/plugins/woocommerce/src/Internal/Admin/ProductReviews/ReviewsCommentsOverrides.php index 99183f771ba..b54e9c10b3a 100644 --- a/plugins/woocommerce/src/Internal/Admin/ProductReviews/ReviewsCommentsOverrides.php +++ b/plugins/woocommerce/src/Internal/Admin/ProductReviews/ReviewsCommentsOverrides.php @@ -4,6 +4,7 @@ namespace Automattic\WooCommerce\Internal\Admin\ProductReviews; use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods; use WP_Comment_Query; +use WP_Screen; /** * Tweaks the WordPress comments page to exclude reviews. @@ -116,6 +117,12 @@ class ReviewsCommentsOverrides { * @return array */ protected function exclude_reviews_from_comments( $args ) : array { + $screen = get_current_screen(); + + // We only wish to intervene if the edit comments screen has been requested. + if ( ! $screen instanceof WP_Screen || 'edit-comments' !== $screen->id ) { + return $args; + } if ( ! empty( $args['post_type'] ) && $args['post_type'] !== 'any' ) { $post_types = (array) $args['post_type']; diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php index 0eada0ee77c..e7635503797 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php @@ -7,6 +7,7 @@ use Generator; use ReflectionClass; use ReflectionException; use WC_Unit_Test_Case; +use WP_Screen; /** * Tests the product reviews overrides for the comments page. @@ -244,20 +245,43 @@ class ReviewsCommentsOverridesTest extends WC_Unit_Test_Case { * @throws ReflectionException If the method doesn't exist. */ public function test_exclude_reviews_from_comments() : void { + global $current_screen; + $original_screen_value = $current_screen; + $overrides = wc_get_container()->get( ReviewsCommentsOverrides::class ); + $method = ( new ReflectionClass( $overrides ) )->getMethod( 'exclude_reviews_from_comments' ); + $method->setAccessible( true ); $original_args = [ 'post_type' => [ 'product' ], ]; - $this->assertTrue( in_array( 'product', $original_args['post_type'] ) ); + $current_screen = WP_Screen::get( 'edit-comments' ); + $filtered_args = $method->invoke( $overrides, $original_args ); - $method = ( new ReflectionClass( $overrides ) )->getMethod( 'exclude_reviews_from_comments' ); - $method->setAccessible( true ); + $this->assertFalse( + in_array( 'product', $filtered_args['post_type'] ), + 'In the context of the edit-comments screen, the product post type will be removed from the comments query.' + ); - $new_args = $method->invoke( $overrides, $original_args ); + $current_screen = WP_Screen::get( 'arbitrary-admin-page' ); + $filtered_args = $method->invoke( $overrides, $original_args ); - $this->assertFalse( in_array( 'product', $new_args['post_type'] ) ); + $this->assertTrue( + in_array( 'product', $filtered_args['post_type'] ), + 'In the context of all other admin screens, the product post type will not be removed from the comments query.' + ); + + $current_screen = null; + $filtered_args = $method->invoke( $overrides, $original_args ); + + $this->assertTrue( + in_array( 'product', $filtered_args['post_type'] ), + 'If the $current_screen global is not set, the product post type will not be removed from the comments query.' + ); + + // Clean-up. + $current_screen = $original_screen_value; } } From d4738cd5f0807ab77d9adb00f17287e64c5aa331 Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 27 Mar 2023 11:12:34 -0700 Subject: [PATCH 064/466] Changelog. --- .../woocommerce/changelog/fix-34594-product-editor-reviews | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/fix-34594-product-editor-reviews diff --git a/plugins/woocommerce/changelog/fix-34594-product-editor-reviews b/plugins/woocommerce/changelog/fix-34594-product-editor-reviews new file mode 100644 index 00000000000..f3ffd7d5e56 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-34594-product-editor-reviews @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Restores comments (reviews) to the product editor. From 03ebff86b880fc84fc4281912adecc318863dd9c Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 27 Mar 2023 13:15:42 -0700 Subject: [PATCH 065/466] Quick edit and reply customizations targeting the Product Review page should not impact the product editor. --- .../src/Internal/Admin/ProductReviews/Reviews.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/woocommerce/src/Internal/Admin/ProductReviews/Reviews.php b/plugins/woocommerce/src/Internal/Admin/ProductReviews/Reviews.php index 22109694091..55d9e77d2a6 100644 --- a/plugins/woocommerce/src/Internal/Admin/ProductReviews/Reviews.php +++ b/plugins/woocommerce/src/Internal/Admin/ProductReviews/Reviews.php @@ -168,6 +168,11 @@ class Reviews { * @return void */ private function handle_edit_review(): void { + // Don't interfere with comment functionality relating to the reviews meta box within the product editor. + if ( sanitize_text_field( wp_unslash( $_POST['mode'] ) ) === 'single' ) { + return; + } + check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' ); $comment_id = isset( $_POST['comment_ID'] ) ? (int) sanitize_text_field( wp_unslash( $_POST['comment_ID'] ) ) : 0; @@ -235,6 +240,11 @@ class Reviews { * @return void */ private function handle_reply_to_review() : void { + // Don't interfere with comment functionality relating to the reviews meta box within the product editor. + if ( sanitize_text_field( wp_unslash( $_POST['mode'] ) ) === 'single' ) { + return; + } + check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' ); $comment_post_ID = isset( $_POST['comment_post_ID'] ) ? (int) sanitize_text_field( wp_unslash( $_POST['comment_post_ID'] ) ) : 0; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase From c567a679b57a55ce0256f203bd82406b209a3c8a Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 27 Mar 2023 13:21:25 -0700 Subject: [PATCH 066/466] Do not assume is set in the array. --- .../woocommerce/src/Internal/Admin/ProductReviews/Reviews.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/woocommerce/src/Internal/Admin/ProductReviews/Reviews.php b/plugins/woocommerce/src/Internal/Admin/ProductReviews/Reviews.php index 55d9e77d2a6..8246e4075b5 100644 --- a/plugins/woocommerce/src/Internal/Admin/ProductReviews/Reviews.php +++ b/plugins/woocommerce/src/Internal/Admin/ProductReviews/Reviews.php @@ -169,7 +169,7 @@ class Reviews { */ private function handle_edit_review(): void { // Don't interfere with comment functionality relating to the reviews meta box within the product editor. - if ( sanitize_text_field( wp_unslash( $_POST['mode'] ) ) === 'single' ) { + if ( sanitize_text_field( wp_unslash( $_POST['mode'] ?? '' ) ) === 'single' ) { return; } @@ -241,7 +241,7 @@ class Reviews { */ private function handle_reply_to_review() : void { // Don't interfere with comment functionality relating to the reviews meta box within the product editor. - if ( sanitize_text_field( wp_unslash( $_POST['mode'] ) ) === 'single' ) { + if ( sanitize_text_field( wp_unslash( $_POST['mode'] ?? '' ) ) === 'single' ) { return; } From 44330f5ab8bbc2621747004d071dc874ad055d17 Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 27 Mar 2023 13:26:23 -0700 Subject: [PATCH 067/466] Tidy tests per coding standards. --- .../ProductReviews/ReviewsCommentsOverridesTest.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php index e7635503797..1f826c2e8e9 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php @@ -256,27 +256,30 @@ class ReviewsCommentsOverridesTest extends WC_Unit_Test_Case { 'post_type' => [ 'product' ], ]; + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited $current_screen = WP_Screen::get( 'edit-comments' ); $filtered_args = $method->invoke( $overrides, $original_args ); $this->assertFalse( - in_array( 'product', $filtered_args['post_type'] ), + in_array( 'product', $filtered_args['post_type'], true ), 'In the context of the edit-comments screen, the product post type will be removed from the comments query.' ); + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited $current_screen = WP_Screen::get( 'arbitrary-admin-page' ); $filtered_args = $method->invoke( $overrides, $original_args ); $this->assertTrue( - in_array( 'product', $filtered_args['post_type'] ), + in_array( 'product', $filtered_args['post_type'], true ), 'In the context of all other admin screens, the product post type will not be removed from the comments query.' ); + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited $current_screen = null; $filtered_args = $method->invoke( $overrides, $original_args ); $this->assertTrue( - in_array( 'product', $filtered_args['post_type'] ), + in_array( 'product', $filtered_args['post_type'], true ), 'If the $current_screen global is not set, the product post type will not be removed from the comments query.' ); From 7c0dfa309382c6849320a4c880307d173b026e29 Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 27 Mar 2023 13:32:05 -0700 Subject: [PATCH 068/466] One more PHPCS override. We need to restore the current screen obj. --- .../Admin/ProductReviews/ReviewsCommentsOverridesTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php index 1f826c2e8e9..696e080caf6 100644 --- a/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/Admin/ProductReviews/ReviewsCommentsOverridesTest.php @@ -283,7 +283,7 @@ class ReviewsCommentsOverridesTest extends WC_Unit_Test_Case { 'If the $current_screen global is not set, the product post type will not be removed from the comments query.' ); - // Clean-up. + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited $current_screen = $original_screen_value; } From e18a531e85b2a8c5e667526844f14d388b0c649a Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Tue, 28 Mar 2023 11:43:33 +0800 Subject: [PATCH 069/466] Add Payoneer, zipco payment gateways and update Klarna available countries (#37329) * Update default payment gateways * Add changelog * Update payoneer image --- .../assets/images/onboarding/payoneer.png | Bin 0 -> 11080 bytes .../assets/images/onboarding/zipco.png | Bin 0 -> 7210 bytes .../images/payment_methods/72x72/payoneer.png | Bin 0 -> 8194 bytes .../images/payment_methods/72x72/zipco.png | Bin 0 -> 2730 bytes .../changelog/update-payment-partners | 4 ++ .../DefaultPaymentGateways.php | 54 ++++++++++++++++++ 6 files changed, 58 insertions(+) create mode 100644 plugins/woocommerce/assets/images/onboarding/payoneer.png create mode 100644 plugins/woocommerce/assets/images/onboarding/zipco.png create mode 100644 plugins/woocommerce/assets/images/payment_methods/72x72/payoneer.png create mode 100644 plugins/woocommerce/assets/images/payment_methods/72x72/zipco.png create mode 100644 plugins/woocommerce/changelog/update-payment-partners diff --git a/plugins/woocommerce/assets/images/onboarding/payoneer.png b/plugins/woocommerce/assets/images/onboarding/payoneer.png new file mode 100644 index 0000000000000000000000000000000000000000..4f109263dd37963788be24542985bc28004a20ac GIT binary patch literal 11080 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 jzvpmEmmWWHvQ&RNI>jOWNWS#ncOYPA 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 literal 0 HcmV?d00001 diff --git a/plugins/woocommerce/assets/images/payment_methods/72x72/payoneer.png b/plugins/woocommerce/assets/images/payment_methods/72x72/payoneer.png new file mode 100644 index 0000000000000000000000000000000000000000..49df118b1c8db6e5e294cbcc757a6e6061f0d1b5 GIT binary patch literal 8194 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 literal 0 HcmV?d00001 diff --git a/plugins/woocommerce/assets/images/payment_methods/72x72/zipco.png b/plugins/woocommerce/assets/images/payment_methods/72x72/zipco.png new file mode 100644 index 0000000000000000000000000000000000000000..00867047ef99e2475a094c10d07a083f4a157947 GIT binary patch literal 2730 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 7, 'affirm' => 8, 'afterpay' => 9, + 'zipmoney' => 10, + 'payoneer-checkout' => 11, ); /** @@ -227,6 +229,9 @@ class DefaultPaymentGateways { 'is_visible' => array( self::get_rules_for_countries( array( + 'MX', + 'US', + 'CA', 'AT', 'BE', 'CH', @@ -241,12 +246,15 @@ class DefaultPaymentGateways { 'NO', 'PL', 'SE', + 'NZ', + 'AU', ) ), self::get_rules_for_cbd( false ), ), 'category_other' => array(), 'category_additional' => array( + 'MX', 'US', 'CA', 'AT', @@ -263,6 +271,8 @@ class DefaultPaymentGateways { 'NO', 'PL', 'SE', + 'NZ', + 'AU', ), ), array( @@ -318,6 +328,27 @@ class DefaultPaymentGateways { 'category_other' => array( 'ZA' ), 'category_additional' => array(), ), + array( + 'id' => 'payoneer-checkout', + 'title' => __( 'Payoneer Checkout', 'woocommerce' ), + 'content' => __( 'Payoneer Checkout is the next generation of payment processing platforms, giving merchants around the world the solutions and direction they need to succeed in today’s hyper-competitive global market.', 'woocommerce' ), + 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/payoneer.png', + 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/payoneer.png', + 'plugins' => array( 'payoneer-checkout' ), + 'is_visible' => array( + self::get_rules_for_countries( + array( + 'HK', + 'CN', + ) + ), + ), + 'category_other' => array(), + 'category_additional' => array( + 'HK', + 'CN', + ), + ), array( 'id' => 'paystack', 'title' => __( 'Paystack', 'woocommerce' ), @@ -776,6 +807,29 @@ class DefaultPaymentGateways { ), ), ), + array( + 'id' => 'zipmoney', + 'title' => __( 'Zip Co - Buy Now, Pay Later', 'woocommerce' ), + 'content' => __( 'Give your customers the power to pay later, interest free and watch your sales grow.', 'woocommerce' ), + 'image' => WC_ADMIN_IMAGES_FOLDER_URL . '/onboarding/zipco.png', + 'image_72x72' => WC_ADMIN_IMAGES_FOLDER_URL . '/payment_methods/72x72/zipco.png', + 'plugins' => array( 'zipmoney-payments-woocommerce' ), + 'is_visible' => array( + self::get_rules_for_countries( + array( + 'US', + 'NZ', + 'AU', + ) + ), + ), + 'category_other' => array(), + 'category_additional' => array( + 'US', + 'NZ', + 'AU', + ), + ), ); foreach ( $payment_gateways as $index => $payment_gateway ) { From 9eeda1d8533fcab099c88032a05ea4f7604f62d3 Mon Sep 17 00:00:00 2001 From: Niels Lange Date: Tue, 28 Mar 2023 12:05:58 +0700 Subject: [PATCH 070/466] Move .wp-env.json.mustache to root folder --- .../js/extend-cart-checkout-block => }/.wp-env.json.mustache | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/js/extend-cart-checkout-block/{packages/js/extend-cart-checkout-block => }/.wp-env.json.mustache (100%) diff --git a/packages/js/extend-cart-checkout-block/packages/js/extend-cart-checkout-block/.wp-env.json.mustache b/packages/js/extend-cart-checkout-block/.wp-env.json.mustache similarity index 100% rename from packages/js/extend-cart-checkout-block/packages/js/extend-cart-checkout-block/.wp-env.json.mustache rename to packages/js/extend-cart-checkout-block/.wp-env.json.mustache From cdeb32baa13acf93934c8d417bab942067d2c238 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Tue, 28 Mar 2023 14:18:25 +0800 Subject: [PATCH 071/466] Add `country` query param to payment suggestion data sources (#37443) * Add country query param to payment gateway data sources * Add changelog * Fix lint --- .../add-country-code-remote-payment-suggestion | 4 ++++ .../PaymentGatewaySuggestionsDataSourcePoller.php | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 plugins/woocommerce/changelog/add-country-code-remote-payment-suggestion diff --git a/plugins/woocommerce/changelog/add-country-code-remote-payment-suggestion b/plugins/woocommerce/changelog/add-country-code-remote-payment-suggestion new file mode 100644 index 00000000000..5cf797795b3 --- /dev/null +++ b/plugins/woocommerce/changelog/add-country-code-remote-payment-suggestion @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +Add country query param to payment gateway data sources diff --git a/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/PaymentGatewaySuggestionsDataSourcePoller.php b/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/PaymentGatewaySuggestionsDataSourcePoller.php index 285f3ac76ef..7d1b87d5a40 100644 --- a/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/PaymentGatewaySuggestionsDataSourcePoller.php +++ b/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/PaymentGatewaySuggestionsDataSourcePoller.php @@ -33,7 +33,20 @@ class PaymentGatewaySuggestionsDataSourcePoller extends DataSourcePoller { */ public static function get_instance() { if ( ! self::$instance ) { - self::$instance = new self( self::ID, self::DATA_SOURCES ); + // Add country query param to data sources. + $base_location = wc_get_base_location(); + $data_sources = array_map( + function( $url ) use ( $base_location ) { + return add_query_arg( + 'country', + $base_location['country'], + $url + ); + }, + self::DATA_SOURCES + ); + + self::$instance = new self( self::ID, $data_sources ); } return self::$instance; } From bc42b853d83c8d5b29f8a340a6774c46cabea3cc Mon Sep 17 00:00:00 2001 From: louwie17 Date: Tue, 28 Mar 2023 04:15:17 -0300 Subject: [PATCH 072/466] Update product editor interpolate components (#37453) * Remove use cases of `interpolateComponents` * Remove from package json * Add changelog --- ...7312_product_editor_interpolate_components | 4 ++++ packages/js/product-editor/package.json | 3 +-- .../create-category-modal.tsx | 20 ++++++++-------- .../details-feature-field.tsx | 19 ++++++++------- .../components/details-name-block/edit.tsx | 13 +++++------ .../details-name-field/details-name-field.tsx | 17 ++++++++------ .../edit-product-link-modal.tsx | 17 ++++++++------ .../src/components/pricing-block/edit.tsx | 23 ++++++++++--------- pnpm-lock.yaml | 16 ++++++------- 9 files changed, 71 insertions(+), 61 deletions(-) create mode 100644 packages/js/product-editor/changelog/update-37312_product_editor_interpolate_components diff --git a/packages/js/product-editor/changelog/update-37312_product_editor_interpolate_components b/packages/js/product-editor/changelog/update-37312_product_editor_interpolate_components new file mode 100644 index 00000000000..6ad5335447e --- /dev/null +++ b/packages/js/product-editor/changelog/update-37312_product_editor_interpolate_components @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Remove use of @automattic/interpolate-components using createInterpolateElement instead. diff --git a/packages/js/product-editor/package.json b/packages/js/product-editor/package.json index 90056bb980a..d124e742dba 100644 --- a/packages/js/product-editor/package.json +++ b/packages/js/product-editor/package.json @@ -28,7 +28,6 @@ "access": "public" }, "dependencies": { - "@automattic/interpolate-components": "^1.2.0", "@types/lodash": "^4.14.179", "@types/wordpress__blocks": "^11.0.7", "@woocommerce/admin-layout": "workspace:*", @@ -79,8 +78,8 @@ "@types/wordpress__media-utils": "^3.0.0", "@types/wordpress__plugins": "^3.0.0", "@woocommerce/eslint-plugin": "workspace:*", - "@woocommerce/internal-style-build": "workspace:*", "@woocommerce/internal-js-tests": "workspace:*", + "@woocommerce/internal-style-build": "workspace:*", "@wordpress/block-editor": "^9.8.0", "@wordpress/browserslist-config": "wp-6.0", "concurrently": "^7.0.0", diff --git a/packages/js/product-editor/src/components/details-categories-field/create-category-modal.tsx b/packages/js/product-editor/src/components/details-categories-field/create-category-modal.tsx index 950fae6623b..6faebdd826a 100644 --- a/packages/js/product-editor/src/components/details-categories-field/create-category-modal.tsx +++ b/packages/js/product-editor/src/components/details-categories-field/create-category-modal.tsx @@ -2,10 +2,13 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import interpolateComponents from '@automattic/interpolate-components'; import { Button, Modal, Spinner, TextControl } from '@wordpress/components'; import { useDebounce } from '@wordpress/compose'; -import { useState, createElement } from '@wordpress/element'; +import { + useState, + createElement, + createInterpolateElement, +} from '@wordpress/element'; import { useDispatch } from '@wordpress/data'; import { __experimentalSelectControl as SelectControl, @@ -93,19 +96,16 @@ export const CreateCategoryModal: React.FC< CreateCategoryModalProps > = ( { /> > items={ categoriesSelectList } - label={ interpolateComponents( { - mixedString: __( - 'Parent category {{optional/}}', - 'woocommerce' - ), - components: { + label={ createInterpolateElement( + __( 'Parent category ', 'woocommerce' ), + { optional: ( { __( '(optional)', 'woocommerce' ) } ), - }, - } ) } + } + ) } selected={ categoryParent } onSelect={ ( item ) => item && setCategoryParent( item ) } onRemove={ () => setCategoryParent( null ) } diff --git a/packages/js/product-editor/src/components/details-feature-field/details-feature-field.tsx b/packages/js/product-editor/src/components/details-feature-field/details-feature-field.tsx index 5d256f69ce1..1821c2e41f4 100644 --- a/packages/js/product-editor/src/components/details-feature-field/details-feature-field.tsx +++ b/packages/js/product-editor/src/components/details-feature-field/details-feature-field.tsx @@ -8,10 +8,13 @@ import { Link, __experimentalTooltip as Tooltip, } from '@woocommerce/components'; -import interpolateComponents from '@automattic/interpolate-components'; import { Product } from '@woocommerce/data'; import { recordEvent } from '@woocommerce/tracks'; -import { createElement, Fragment } from '@wordpress/element'; +import { + createElement, + Fragment, + createInterpolateElement, +} from '@wordpress/element'; /** * Internal dependencies @@ -28,12 +31,12 @@ export const DetailsFeatureField = () => { <> { __( 'Feature this product', 'woocommerce' ) } ', 'woocommerce' ), - components: { + { moreLink: ( { { __( 'Learn more', 'woocommerce' ) } ), - }, - } ) } + } + ) } /> } diff --git a/packages/js/product-editor/src/components/details-name-block/edit.tsx b/packages/js/product-editor/src/components/details-name-block/edit.tsx index 3c18544da4b..ea38e9a0703 100644 --- a/packages/js/product-editor/src/components/details-name-block/edit.tsx +++ b/packages/js/product-editor/src/components/details-name-block/edit.tsx @@ -2,8 +2,7 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { createElement } from '@wordpress/element'; -import interpolateComponents from '@automattic/interpolate-components'; +import { createElement, createInterpolateElement } from '@wordpress/element'; import { TextControl } from '@woocommerce/components'; import { useBlockProps } from '@wordpress/block-editor'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -18,16 +17,16 @@ export function Edit() { return (
', 'woocommerce' ), + { required: ( { __( '(required)', 'woocommerce' ) } ), - }, - } ) } + } + ) } name={ 'woocommerce-product-name' } placeholder={ __( 'e.g. 12 oz Coffee Mug', 'woocommerce' ) } onChange={ setName } diff --git a/packages/js/product-editor/src/components/details-name-field/details-name-field.tsx b/packages/js/product-editor/src/components/details-name-field/details-name-field.tsx index 7099b2968da..498c1f016e6 100644 --- a/packages/js/product-editor/src/components/details-name-field/details-name-field.tsx +++ b/packages/js/product-editor/src/components/details-name-field/details-name-field.tsx @@ -6,13 +6,16 @@ import { useSelect } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; import { cleanForSlug } from '@wordpress/url'; import { useFormContext } from '@woocommerce/components'; -import interpolateComponents from '@automattic/interpolate-components'; import { Product, PRODUCTS_STORE_NAME, WCDataSelector, } from '@woocommerce/data'; -import { useState, createElement } from '@wordpress/element'; +import { + useState, + createElement, + createInterpolateElement, +} from '@wordpress/element'; /** * Internal dependencies @@ -55,16 +58,16 @@ export const DetailsNameField = ( {} ) => { return (
', 'woocommerce' ), + { required: ( { __( '(required)', 'woocommerce' ) } ), - }, - } ) } + } + ) } name={ `${ PRODUCT_DETAILS_SLUG }-name` } placeholder={ __( 'e.g. 12 oz Coffee Mug', 'woocommerce' ) } { ...getInputProps( 'name', { diff --git a/packages/js/product-editor/src/components/edit-product-link-modal/edit-product-link-modal.tsx b/packages/js/product-editor/src/components/edit-product-link-modal/edit-product-link-modal.tsx index 602845c8720..fab86bce6f5 100644 --- a/packages/js/product-editor/src/components/edit-product-link-modal/edit-product-link-modal.tsx +++ b/packages/js/product-editor/src/components/edit-product-link-modal/edit-product-link-modal.tsx @@ -3,10 +3,13 @@ */ import { __ } from '@wordpress/i18n'; import { Button, Modal, TextControl } from '@wordpress/components'; -import { useState, createElement } from '@wordpress/element'; +import { + useState, + createElement, + createInterpolateElement, +} from '@wordpress/element'; import { useDispatch } from '@wordpress/data'; import { cleanForSlug } from '@wordpress/url'; -import interpolateComponents from '@automattic/interpolate-components'; import { Product } from '@woocommerce/data'; import { useFormContext } from '@woocommerce/components'; import { recordEvent } from '@woocommerce/tracks'; @@ -106,12 +109,12 @@ export const EditProductLinkModal: React.FC< EditProductLinkModalProps > = ( { value={ slug } onChange={ setSlug } hideLabelFromVision - help={ interpolateComponents( { - mixedString: __( 'Preview: {{link/}}', 'woocommerce' ), - components: { + help={ createInterpolateElement( + __( 'Preview: ', 'woocommerce' ), + { link: { newProductLinkLabel }, - }, - } ) } + } + ) } />
- + Date: Tue, 28 Mar 2023 19:11:20 +0530 Subject: [PATCH 076/466] Applied standards. --- .../DataStores/Orders/OrdersTableSearchQuery.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableSearchQuery.php b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableSearchQuery.php index 2ae18e4345a..a64cbc7fdaf 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableSearchQuery.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableSearchQuery.php @@ -80,7 +80,7 @@ class OrdersTableSearchQuery { private function generate_where(): string { $where = ''; $possible_order_id = (string) absint( $this->query->get( 's' ) ); - $order_table = $this->query->get_table_name( 'orders' ); + $order_table = $this->query->get_table_name( 'orders' ); // Support the passing of an order ID as the search term. if ( (string) $this->query->get( 's' ) === $possible_order_id ) { @@ -107,10 +107,9 @@ class OrdersTableSearchQuery { * @return string The where clause for meta table. */ private function generate_where_for_meta_table(): string { - $meta_table = $this->query->get_table_name( 'meta' ); - $meta_fields = $this->get_meta_fields_to_be_searched(); - return -" + $meta_table = $this->query->get_table_name( 'meta' ); + $meta_fields = $this->get_meta_fields_to_be_searched(); + return " SELECT search_query_meta.order_id FROM $meta_table as search_query_meta WHERE search_query_meta.meta_key IN ( $meta_fields ) From a92c16d93d8820dec347d9c48d68230828ea9049 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Tue, 28 Mar 2023 09:50:15 -0400 Subject: [PATCH 077/466] Update style of product attributes tab empty state (#37429) * Update style of product attributes tab empty state * Update icon --- .../images/icons/global-attributes-icon.svg | 6 +-- .../update-product-attributes-empty-state | 4 ++ .../woocommerce/client/legacy/css/admin.scss | 41 +++++++++++-------- .../views/html-product-data-attributes.php | 26 ++++++------ 4 files changed, 45 insertions(+), 32 deletions(-) create mode 100644 plugins/woocommerce/changelog/update-product-attributes-empty-state diff --git a/plugins/woocommerce/assets/images/icons/global-attributes-icon.svg b/plugins/woocommerce/assets/images/icons/global-attributes-icon.svg index bbb5d8122d7..7e567300f87 100644 --- a/plugins/woocommerce/assets/images/icons/global-attributes-icon.svg +++ b/plugins/woocommerce/assets/images/icons/global-attributes-icon.svg @@ -1,8 +1,8 @@ - - + + - + diff --git a/plugins/woocommerce/changelog/update-product-attributes-empty-state b/plugins/woocommerce/changelog/update-product-attributes-empty-state new file mode 100644 index 00000000000..204f428b324 --- /dev/null +++ b/plugins/woocommerce/changelog/update-product-attributes-empty-state @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak + +Update style of product attributes tab empty state. diff --git a/plugins/woocommerce/client/legacy/css/admin.scss b/plugins/woocommerce/client/legacy/css/admin.scss index d6f3bc4a92c..45cdcdd07b7 100644 --- a/plugins/woocommerce/client/legacy/css/admin.scss +++ b/plugins/woocommerce/client/legacy/css/admin.scss @@ -1020,29 +1020,36 @@ #product_attributes { .add-global-attribute-container { - box-sizing: border-box; display: flex; flex-direction: column; - justify-content: center; - align-items: center; - padding: 32px 0px; - gap: 24px; height: 360px; - @media screen and ( max-width: 782px ) { - button { - vertical-align: top; - } - } - p { - width: 90%; - max-width: 544px; - font-size: 14px; - line-height: 18px; - text-align: center; - } + &.hidden { display: none; } + + .message { + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 32px 0px; + gap: 24px; + flex: 1; + @media screen and ( max-width: 782px ) { + button { + vertical-align: top; + } + } + p { + width: 90%; + max-width: 544px; + font-size: 14px; + line-height: 18px; + text-align: center; + } + } } .toolbar-top { .button, diff --git a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-attributes.php b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-attributes.php index 7b7915fdcbc..1858a0c3b45 100644 --- a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-attributes.php +++ b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-attributes.php @@ -22,20 +22,22 @@ $icon_url = WC_ADMIN_IMAGES_FOLDER_URL . '/icons/global-a
- + Date: Tue, 28 Mar 2023 12:53:29 -0700 Subject: [PATCH 084/466] Changelog. --- plugins/woocommerce/changelog/fix-27518-payment-gateway-title | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/fix-27518-payment-gateway-title diff --git a/plugins/woocommerce/changelog/fix-27518-payment-gateway-title b/plugins/woocommerce/changelog/fix-27518-payment-gateway-title new file mode 100644 index 00000000000..654ab0ba5dc --- /dev/null +++ b/plugins/woocommerce/changelog/fix-27518-payment-gateway-title @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Avoid over-aggressive escaping of the payment gateway title in the context of the checkout thank you page. From 59c977e5e933a4492fc248547b4b77dc951d16cc Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Tue, 28 Mar 2023 13:19:57 -0700 Subject: [PATCH 085/466] Bump template version. --- plugins/woocommerce/templates/order/order-details.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/templates/order/order-details.php b/plugins/woocommerce/templates/order/order-details.php index faed60d6a5c..667eac1ae07 100644 --- a/plugins/woocommerce/templates/order/order-details.php +++ b/plugins/woocommerce/templates/order/order-details.php @@ -12,7 +12,7 @@ * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce\Templates - * @version 4.6.0 + * @version 7.7.0 */ use Automattic\WooCommerce\Internal\Utilities\HtmlSanitizer; From 49cde4cad6986f0e4b503de70bbb7cd727057104 Mon Sep 17 00:00:00 2001 From: Joshua T Flowers Date: Tue, 28 Mar 2023 15:18:00 -0700 Subject: [PATCH 086/466] Content lock all blocks in the product editor (#37382) * Lock all blocks in the product editor to content only * Add changelog entry * Add content role to summary field to allow editing * Remove unused BlockControls --- packages/js/product-editor/changelog/fix-37209 | 4 ++++ .../src/components/details-name-block/block.json | 3 ++- .../src/components/details-summary-block/block.json | 4 ++++ .../product-editor/src/components/pricing-block/block.json | 3 ++- packages/js/product-editor/src/components/tab/block.json | 7 +++++-- packages/js/product-editor/src/components/tab/edit.tsx | 6 ++++-- packages/js/product-editor/src/components/tab/style.scss | 5 +++++ 7 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 packages/js/product-editor/changelog/fix-37209 diff --git a/packages/js/product-editor/changelog/fix-37209 b/packages/js/product-editor/changelog/fix-37209 new file mode 100644 index 00000000000..79a64266bd5 --- /dev/null +++ b/packages/js/product-editor/changelog/fix-37209 @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Content lock all blocks in the product editor diff --git a/packages/js/product-editor/src/components/details-name-block/block.json b/packages/js/product-editor/src/components/details-name-block/block.json index 920e5956645..15db681d2a1 100644 --- a/packages/js/product-editor/src/components/details-name-block/block.json +++ b/packages/js/product-editor/src/components/details-name-block/block.json @@ -9,7 +9,8 @@ "textdomain": "default", "attributes": { "name": { - "type": "string" + "type": "string", + "__experimentalRole": "content" } }, "supports": { diff --git a/packages/js/product-editor/src/components/details-summary-block/block.json b/packages/js/product-editor/src/components/details-summary-block/block.json index 003cd6841e9..dd1fa7f8a89 100644 --- a/packages/js/product-editor/src/components/details-summary-block/block.json +++ b/packages/js/product-editor/src/components/details-summary-block/block.json @@ -17,6 +17,10 @@ }, "label": { "type": "string" + }, + "content": { + "type": "string", + "__experimentalRole": "content" } }, "supports": { diff --git a/packages/js/product-editor/src/components/pricing-block/block.json b/packages/js/product-editor/src/components/pricing-block/block.json index 9ca3884ba5f..90851b04cf9 100644 --- a/packages/js/product-editor/src/components/pricing-block/block.json +++ b/packages/js/product-editor/src/components/pricing-block/block.json @@ -9,7 +9,8 @@ "textdomain": "default", "attributes": { "name": { - "type": "string" + "type": "string", + "__experimentalRole": "content" }, "label": { "type": "string" diff --git a/packages/js/product-editor/src/components/tab/block.json b/packages/js/product-editor/src/components/tab/block.json index f9edf354676..67a35ed4972 100644 --- a/packages/js/product-editor/src/components/tab/block.json +++ b/packages/js/product-editor/src/components/tab/block.json @@ -21,7 +21,10 @@ "multiple": true, "reusable": false, "inserter": false, - "lock": false + "lock": false, + "__experimentalToolbar": false }, - "usesContext": [ "selectedTab" ] + "usesContext": [ "selectedTab" ], + "editorStyle": "file:./editor.css", + "templateLock": "contentOnly" } diff --git a/packages/js/product-editor/src/components/tab/edit.tsx b/packages/js/product-editor/src/components/tab/edit.tsx index 226dbd92ea8..2147a97578a 100644 --- a/packages/js/product-editor/src/components/tab/edit.tsx +++ b/packages/js/product-editor/src/components/tab/edit.tsx @@ -1,9 +1,9 @@ /** * External dependencies */ +import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; import classnames from 'classnames'; import { createElement } from '@wordpress/element'; -import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; import type { BlockAttributes } from '@wordpress/blocks'; /** @@ -39,7 +39,9 @@ export function Edit( { role="tabpanel" className={ classes } > - + { /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ } + { /* @ts-ignore Content only template locking does exist for this property. */ } + ); diff --git a/packages/js/product-editor/src/components/tab/style.scss b/packages/js/product-editor/src/components/tab/style.scss index 867fff9ee9a..d82fc701dbd 100644 --- a/packages/js/product-editor/src/components/tab/style.scss +++ b/packages/js/product-editor/src/components/tab/style.scss @@ -2,4 +2,9 @@ &:not(.is-selected) { display: none; } +} + +// Remove the Gutenberg selected outline. +.wp-block-woocommerce-product-tab:after { + display: none; } \ No newline at end of file From 1f714e8ade3b1a65d5f744be4e02ef39c4469201 Mon Sep 17 00:00:00 2001 From: smallfishes Date: Tue, 28 Mar 2023 15:24:02 -0700 Subject: [PATCH 087/466] =?UTF-8?q?fixed=20bug=20where=20adjust=5Fdownload?= =?UTF-8?q?=5Fpermissions=20was=20being=20scheduled=20on=20va=E2=80=A6=20(?= =?UTF-8?q?#34828)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fixed bug where adjust_download_permissions was being scheduled on variable products without downloadable variations * check if is null per code review comment and issue 35918 on GitHub * clean up changelog file * Trigger checks * Fix DownloadPermissionsAdjuster tests --------- Co-authored-by: smallfishes Co-authored-by: Jorge A. Torres --- .../changelog/fix-32316-download-adjuster | 4 ++++ .../Internal/DownloadPermissionsAdjuster.php | 13 +++++++++++ .../DownloadPermissionsAdjusterTest.php | 23 +++++++++++++++++-- 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 plugins/woocommerce/changelog/fix-32316-download-adjuster diff --git a/plugins/woocommerce/changelog/fix-32316-download-adjuster b/plugins/woocommerce/changelog/fix-32316-download-adjuster new file mode 100644 index 00000000000..89f94aad1d5 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-32316-download-adjuster @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +fixed bug where adjust_download_permissions was being scheduled on variable products without downloadable variations diff --git a/plugins/woocommerce/src/Internal/DownloadPermissionsAdjuster.php b/plugins/woocommerce/src/Internal/DownloadPermissionsAdjuster.php index f2f3adb5949..dc489c745b6 100644 --- a/plugins/woocommerce/src/Internal/DownloadPermissionsAdjuster.php +++ b/plugins/woocommerce/src/Internal/DownloadPermissionsAdjuster.php @@ -44,6 +44,19 @@ class DownloadPermissionsAdjuster { return; } + $are_any_children_downloadable = false; + foreach ( $children_ids as $child_id ) { + $child = wc_get_product( $child_id ); + if ( $child && $child->is_downloadable() ) { + $are_any_children_downloadable = true; + break; + } + } + + if ( ! $product->is_downloadable() && ! $are_any_children_downloadable ) { + return; + } + $scheduled_action_args = array( $product->get_id() ); $already_scheduled_actions = diff --git a/plugins/woocommerce/tests/php/src/Internal/DownloadPermissionsAdjusterTest.php b/plugins/woocommerce/tests/php/src/Internal/DownloadPermissionsAdjusterTest.php index 7f31dd09a3a..65ac309d8f2 100644 --- a/plugins/woocommerce/tests/php/src/Internal/DownloadPermissionsAdjusterTest.php +++ b/plugins/woocommerce/tests/php/src/Internal/DownloadPermissionsAdjusterTest.php @@ -102,7 +102,7 @@ class DownloadPermissionsAdjusterTest extends \WC_Unit_Test_Case { ) ); - $product = ProductHelper::create_variation_product(); + $product = $this->create_downloadable_variation_product(); $this->sut->maybe_schedule_adjust_download_permissions( $product ); $expected_get_scheduled_actions_args = array( @@ -135,7 +135,7 @@ class DownloadPermissionsAdjusterTest extends \WC_Unit_Test_Case { ) ); - $product = ProductHelper::create_variation_product(); + $product = $this->create_downloadable_variation_product(); $this->sut->maybe_schedule_adjust_download_permissions( $product ); $expected_get_scheduled_actions_args = array( @@ -168,6 +168,7 @@ class DownloadPermissionsAdjusterTest extends \WC_Unit_Test_Case { $parent_download_id = current( $product->get_downloads() )->get_id(); $child = wc_get_product( current( $product->get_children() ) ); + $child->set_downloadable( true ); $child->set_downloads( array( $download ) ); $child->save(); $child_download_id = current( $child->get_downloads() )->get_id(); @@ -222,6 +223,7 @@ class DownloadPermissionsAdjusterTest extends \WC_Unit_Test_Case { $parent_download_id = current( $product->get_downloads() )->get_id(); $child = wc_get_product( current( $product->get_children() ) ); + $child->set_downloadable( true ); $child->set_downloads( array( $download ) ); $child->save(); $child_download_id = current( $child->get_downloads() )->get_id(); @@ -278,6 +280,7 @@ class DownloadPermissionsAdjusterTest extends \WC_Unit_Test_Case { $parent_download_id = current( $product->get_downloads() )->get_id(); $child = wc_get_product( current( $product->get_children() ) ); + $child->set_downloadable( true ); $child->set_downloads( array( $download ) ); $child->save(); $child_download_id = current( $child->get_downloads() )->get_id(); @@ -359,4 +362,20 @@ class DownloadPermissionsAdjusterTest extends \WC_Unit_Test_Case { return $data_store; } + + /** + * Creates a variable product with a downloadable variation. No downloads are added. + * + * @return \WC_Product A product. + */ + private function create_downloadable_variation_product() { + $product = ProductHelper::create_variation_product(); + + $child = wc_get_product( current( $product->get_children() ) ); + $child->set_downloadable( true ); + $child->save(); + + return $product; + } + } From 19a119f0a83c68ce8cac794935d3aa05aad5156d Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Wed, 29 Mar 2023 17:18:43 +0800 Subject: [PATCH 088/466] Upstream changes from experimental-select-control back to @woocommerce/components (#36521) * Add scrollIntoViewOnOpen and position props to menu * Add ability to customize active item style * Add menu toggle button * Add changelog * Fix changelog * Rename toggle button classname * Change default menu position * Update toggle button story * Add default value to getToggleButtonProps --- .../update-experimental-select-control | 4 +++ .../combo-box.scss | 15 +++++++++++ .../experimental-select-control/combo-box.tsx | 26 ++++++++++++++++++- .../experimental-select-control/menu-item.tsx | 6 +++-- .../src/experimental-select-control/menu.tsx | 13 +++++++++- .../select-control.tsx | 7 ++++- .../stories/index.tsx | 18 +++++++++++++ 7 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 packages/js/components/changelog/update-experimental-select-control diff --git a/packages/js/components/changelog/update-experimental-select-control b/packages/js/components/changelog/update-experimental-select-control new file mode 100644 index 00000000000..8b315f59b12 --- /dev/null +++ b/packages/js/components/changelog/update-experimental-select-control @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Apply wccom experimental select control changes diff --git a/packages/js/components/src/experimental-select-control/combo-box.scss b/packages/js/components/src/experimental-select-control/combo-box.scss index 481797ab2f0..7faddd52a83 100644 --- a/packages/js/components/src/experimental-select-control/combo-box.scss +++ b/packages/js/components/src/experimental-select-control/combo-box.scss @@ -44,3 +44,18 @@ .woocommerce-experimental-select-control__suffix { align-self: stretch; } + +.woocommerce-experimental-select-control__combox-box-toggle-button { + all: unset; + position: absolute; + right: 6px; + top: 50%; + transform: translateY( -50% ); + > svg { + margin-top: 4px; + } +} + +.woocommerce-experimental-select-control:not( .is-focused ) .woocommerce-experimental-select-control__combox-box-toggle-button { + pointer-events: none; // Prevents the icon from being clickable when the combobox is not focused, because otherwise we get a race condition when clicking on the icon, because focussing the combobox opens the menu, then sequentially the icon toggles it back closed +} diff --git a/packages/js/components/src/experimental-select-control/combo-box.tsx b/packages/js/components/src/experimental-select-control/combo-box.tsx index 91cca1c1ecf..d4a53d32c94 100644 --- a/packages/js/components/src/experimental-select-control/combo-box.tsx +++ b/packages/js/components/src/experimental-select-control/combo-box.tsx @@ -1,21 +1,42 @@ /** * External dependencies */ -import { createElement, MouseEvent, useRef } from 'react'; +import { createElement, MouseEvent, useRef, forwardRef } from 'react'; import classNames from 'classnames'; +import { Icon, chevronDown } from '@wordpress/icons'; type ComboBoxProps = { children?: JSX.Element | JSX.Element[] | null; comboBoxProps: JSX.IntrinsicElements[ 'div' ]; inputProps: JSX.IntrinsicElements[ 'input' ]; + getToggleButtonProps?: () => Omit< + JSX.IntrinsicElements[ 'button' ], + 'ref' + >; suffix?: JSX.Element | null; + showToggleButton?: boolean; }; +const ToggleButton = forwardRef< HTMLButtonElement >( ( props, ref ) => { + // using forwardRef here because getToggleButtonProps injects a ref prop + return ( + + ); +} ); + export const ComboBox = ( { children, comboBoxProps, + getToggleButtonProps = () => ( {} ), inputProps, suffix, + showToggleButton, }: ComboBoxProps ) => { const inputRef = useRef< HTMLInputElement | null >( null ); @@ -72,6 +93,9 @@ export const ComboBox = ( { { suffix } ) } + { showToggleButton && ( + + ) } ); }; diff --git a/packages/js/components/src/experimental-select-control/menu-item.tsx b/packages/js/components/src/experimental-select-control/menu-item.tsx index 2200ceba2ef..ef3dbbd465b 100644 --- a/packages/js/components/src/experimental-select-control/menu-item.tsx +++ b/packages/js/components/src/experimental-select-control/menu-item.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import { createElement, ReactElement } from 'react'; +import { createElement, CSSProperties, ReactElement } from 'react'; /** * Internal dependencies @@ -14,6 +14,7 @@ export type MenuItemProps< ItemType > = { item: ItemType; children: ReactElement | string; getItemProps: getItemPropsType< ItemType >; + activeStyle?: CSSProperties; }; export const MenuItem = < ItemType, >( { @@ -21,11 +22,12 @@ export const MenuItem = < ItemType, >( { getItemProps, index, isActive, + activeStyle = { backgroundColor: '#bde4ff' }, item, }: MenuItemProps< ItemType > ) => { return (
  • diff --git a/packages/js/components/src/experimental-select-control/menu.tsx b/packages/js/components/src/experimental-select-control/menu.tsx index 13680d9349d..691b038af68 100644 --- a/packages/js/components/src/experimental-select-control/menu.tsx +++ b/packages/js/components/src/experimental-select-control/menu.tsx @@ -22,6 +22,8 @@ type MenuProps = { getMenuProps: getMenuPropsType; isOpen: boolean; className?: string; + position?: Popover.Position; + scrollIntoViewOnOpen?: boolean; }; export const Menu = ( { @@ -29,6 +31,8 @@ export const Menu = ( { getMenuProps, isOpen, className, + position = 'bottom right', + scrollIntoViewOnOpen = false, }: MenuProps ) => { const [ boundingRect, setBoundingRect ] = useState< DOMRect >(); const selectControlMenuRef = useRef< HTMLDivElement >( null ); @@ -41,6 +45,13 @@ export const Menu = ( { } }, [ selectControlMenuRef.current ] ); + // Scroll the selected item into view when the menu opens. + useEffect( () => { + if ( isOpen && scrollIntoViewOnOpen ) { + selectControlMenuRef.current?.scrollIntoView(); + } + }, [ isOpen, scrollIntoViewOnOpen ] ); + /* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/click-events-have-key-events */ /* Disabled because of the onmouseup on the ul element below. */ return ( @@ -60,7 +71,7 @@ export const Menu = ( { 'has-results': Children.count( children ) > 0, } ) } - position="bottom right" + position={ position } animate={ false } >
      = { disabled?: boolean; inputProps?: GetInputPropsOptions; suffix?: JSX.Element | null; + showToggleButton?: boolean; /** * This is a feature already implemented in downshift@7.0.0 through the * reducer. In order for us to use it this prop is added temporarily until @@ -123,6 +124,7 @@ function SelectControl< ItemType = DefaultItemType >( { disabled, inputProps = {}, suffix = , + showToggleButton = false, __experimentalOpenMenuOnFocus = false, }: SelectControlProps< ItemType > ) { const [ isFocused, setIsFocused ] = useState( false ); @@ -154,12 +156,13 @@ function SelectControl< ItemType = DefaultItemType >( { } setInputValue( getItemLabel( singleSelectedItem ) ); - }, [ singleSelectedItem ] ); + }, [ getItemLabel, multiple, singleSelectedItem ] ); const { isOpen, getLabelProps, getMenuProps, + getToggleButtonProps, getInputProps, getComboboxProps, highlightedIndex, @@ -256,6 +259,7 @@ function SelectControl< ItemType = DefaultItemType >( { { /* eslint-enable jsx-a11y/label-has-for */ } ( { ...inputProps, } ) } suffix={ suffix } + showToggleButton={ showToggleButton } > <> { children( { diff --git a/packages/js/components/src/experimental-select-control/stories/index.tsx b/packages/js/components/src/experimental-select-control/stories/index.tsx index 80902ed691c..cb1706d98d3 100644 --- a/packages/js/components/src/experimental-select-control/stories/index.tsx +++ b/packages/js/components/src/experimental-select-control/stories/index.tsx @@ -573,6 +573,24 @@ export const CustomSuffix: React.FC = () => { ); }; +export const ToggleButton: React.FC = () => { + const [ selected, setSelected ] = + useState< SelectedType< DefaultItemType > >(); + + return ( + item && setSelected( item ) } + onRemove={ () => setSelected( null ) } + suffix={ null } + showToggleButton={ true } + __experimentalOpenMenuOnFocus={ true } + /> + ); +}; + export default { title: 'WooCommerce Admin/experimental/SelectControl', component: SelectControl, From b5aaa1f9969d7feaaec16c438d177d023e27f7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Wed, 29 Mar 2023 11:57:58 +0200 Subject: [PATCH 089/466] Update WooCommerce Blocks package to 9.8.3 (#37477) --- .../changelog/update-woocommerce-blocks-9.8.3 | 4 ++++ plugins/woocommerce/composer.json | 2 +- plugins/woocommerce/composer.lock | 14 +++++++------- 3 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.3 diff --git a/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.3 b/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.3 new file mode 100644 index 00000000000..974d6e765fd --- /dev/null +++ b/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.3 @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update WooCommerce Blocks to 9.8.3 diff --git a/plugins/woocommerce/composer.json b/plugins/woocommerce/composer.json index 4427465d7b2..3e92aaa3041 100644 --- a/plugins/woocommerce/composer.json +++ b/plugins/woocommerce/composer.json @@ -21,7 +21,7 @@ "maxmind-db/reader": "^1.11", "pelago/emogrifier": "^6.0", "woocommerce/action-scheduler": "3.5.4", - "woocommerce/woocommerce-blocks": "9.8.2" + "woocommerce/woocommerce-blocks": "9.8.3" }, "require-dev": { "automattic/jetpack-changelogger": "^3.3.0", diff --git a/plugins/woocommerce/composer.lock b/plugins/woocommerce/composer.lock index 831d80f0efb..a595f51f08d 100644 --- a/plugins/woocommerce/composer.lock +++ b/plugins/woocommerce/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "48377a0bfe2b30537b522f197ba28984", + "content-hash": "d92c2e109d56bc31d8d8f4e1ec0bbaf1", "packages": [ { "name": "automattic/jetpack-autoloader", @@ -628,16 +628,16 @@ }, { "name": "woocommerce/woocommerce-blocks", - "version": "9.8.2", + "version": "9.8.3", "source": { "type": "git", "url": "https://github.com/woocommerce/woocommerce-blocks.git", - "reference": "61488a0f1d1afc8fd7484e1b277d40afd782e005" + "reference": "9431e10310a9dc89d844bb9d480adab64297b130" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/woocommerce/woocommerce-blocks/zipball/61488a0f1d1afc8fd7484e1b277d40afd782e005", - "reference": "61488a0f1d1afc8fd7484e1b277d40afd782e005", + "url": "https://api.github.com/repos/woocommerce/woocommerce-blocks/zipball/9431e10310a9dc89d844bb9d480adab64297b130", + "reference": "9431e10310a9dc89d844bb9d480adab64297b130", "shasum": "" }, "require": { @@ -683,9 +683,9 @@ ], "support": { "issues": "https://github.com/woocommerce/woocommerce-blocks/issues", - "source": "https://github.com/woocommerce/woocommerce-blocks/tree/v9.8.2" + "source": "https://github.com/woocommerce/woocommerce-blocks/tree/v9.8.3" }, - "time": "2023-03-22T14:39:35+00:00" + "time": "2023-03-28T14:44:30+00:00" } ], "packages-dev": [ From b51d32e6b7a3fe83cdd594917bfb3d20526a932e Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Wed, 29 Mar 2023 19:00:20 +0800 Subject: [PATCH 090/466] Remove unused woocommerce_marketing_overview_welcome_hidden option. See https://github.com/woocommerce/woocommerce/pull/37430#discussion_r1150002880. --- plugins/woocommerce/src/Admin/API/Options.php | 1 - .../woocommerce/src/Internal/Admin/Marketing.php | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/plugins/woocommerce/src/Admin/API/Options.php b/plugins/woocommerce/src/Admin/API/Options.php index 7205f72f711..c37a6058931 100644 --- a/plugins/woocommerce/src/Admin/API/Options.php +++ b/plugins/woocommerce/src/Admin/API/Options.php @@ -167,7 +167,6 @@ class Options extends \WC_REST_Data_Controller { 'woocommerce_task_list_dismissed_tasks', 'woocommerce_setting_payments_recommendations_hidden', 'woocommerce_navigation_favorites_tooltip_hidden', - 'woocommerce_marketing_overview_welcome_hidden', 'woocommerce_admin_transient_notices_queue', 'woocommerce_task_list_welcome_modal_dismissed', 'woocommerce_welcome_from_calypso_modal_dismissed', diff --git a/plugins/woocommerce/src/Internal/Admin/Marketing.php b/plugins/woocommerce/src/Internal/Admin/Marketing.php index 47d4ab241bb..acde214b53d 100644 --- a/plugins/woocommerce/src/Internal/Admin/Marketing.php +++ b/plugins/woocommerce/src/Internal/Admin/Marketing.php @@ -44,7 +44,6 @@ class Marketing { add_action( 'admin_menu', array( $this, 'register_pages' ), 5 ); add_action( 'admin_menu', array( $this, 'add_parent_menu_item' ), 6 ); - add_filter( 'woocommerce_admin_preload_options', array( $this, 'preload_options' ) ); add_filter( 'woocommerce_admin_shared_settings', array( $this, 'component_settings' ), 30 ); } @@ -141,18 +140,6 @@ class Marketing { } } - /** - * Preload options to prime state of the application. - * - * @param array $options Array of options to preload. - * @return array - */ - public function preload_options( $options ) { - $options[] = 'woocommerce_marketing_overview_welcome_hidden'; - - return $options; - } - /** * Add settings for marketing feature. * From 34bd12344841a17837e099cebc6f18ba7ae9ef7f Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Wed, 29 Mar 2023 19:06:40 +0800 Subject: [PATCH 091/466] Remove unused button-style mixins. --- .../js/internal-style-build/abstracts/_mixins.scss | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/js/internal-style-build/abstracts/_mixins.scss b/packages/js/internal-style-build/abstracts/_mixins.scss index 43ab642052d..bbbce36cd95 100644 --- a/packages/js/internal-style-build/abstracts/_mixins.scss +++ b/packages/js/internal-style-build/abstracts/_mixins.scss @@ -102,20 +102,6 @@ cursor: default; } -@mixin button-style__hover { - background-color: $studio-white; - color: $gray-900; - box-shadow: inset 0 0 0 1px $gray-400, inset 0 0 0 2px $studio-white, - 0 1px 1px rgba( $gray-900, 0.2 ); -} - -@mixin button-style__active() { - outline: none; - background-color: $studio-white; - color: $gray-900; - box-shadow: inset 0 0 0 1px $gray-400, inset 0 0 0 2px $studio-white; -} - @mixin button-style__focus-active() { background-color: $studio-white; color: $gray-900; From f814f09cabc9ec341054d960965c4a8fa8e2da10 Mon Sep 17 00:00:00 2001 From: Gan Eng Chin Date: Wed, 29 Mar 2023 19:09:54 +0800 Subject: [PATCH 092/466] Edit changelog. To mention removing classic Marketing page and unused code. --- .../feature-37367-marketing-multichannel-remove-toggle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/changelog/feature-37367-marketing-multichannel-remove-toggle b/plugins/woocommerce/changelog/feature-37367-marketing-multichannel-remove-toggle index 54f79f3f168..a36d836c3dc 100644 --- a/plugins/woocommerce/changelog/feature-37367-marketing-multichannel-remove-toggle +++ b/plugins/woocommerce/changelog/feature-37367-marketing-multichannel-remove-toggle @@ -1,4 +1,4 @@ Significance: minor Type: update -Make Multichannel Marketing the default new UI for Marketing page. +Make Multichannel Marketing the default new UI for Marketing page; remove classic Marketing page and unused code. From 2c0004a78d8519344f991b78f112473329a0da0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Wed, 29 Mar 2023 14:59:28 +0200 Subject: [PATCH 093/466] Update WooCommerce Blocks package to 9.8.4 (#37492) --- .../changelog/update-woocommerce-blocks-9.8.4 | 4 ++++ plugins/woocommerce/composer.json | 2 +- plugins/woocommerce/composer.lock | 14 +++++++------- 3 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.4 diff --git a/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.4 b/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.4 new file mode 100644 index 00000000000..2cb3821d89a --- /dev/null +++ b/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.4 @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update WooCommerce Blocks to 9.8.4 diff --git a/plugins/woocommerce/composer.json b/plugins/woocommerce/composer.json index 3e92aaa3041..cdac34076f3 100644 --- a/plugins/woocommerce/composer.json +++ b/plugins/woocommerce/composer.json @@ -21,7 +21,7 @@ "maxmind-db/reader": "^1.11", "pelago/emogrifier": "^6.0", "woocommerce/action-scheduler": "3.5.4", - "woocommerce/woocommerce-blocks": "9.8.3" + "woocommerce/woocommerce-blocks": "9.8.4" }, "require-dev": { "automattic/jetpack-changelogger": "^3.3.0", diff --git a/plugins/woocommerce/composer.lock b/plugins/woocommerce/composer.lock index a595f51f08d..570a718d4b3 100644 --- a/plugins/woocommerce/composer.lock +++ b/plugins/woocommerce/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d92c2e109d56bc31d8d8f4e1ec0bbaf1", + "content-hash": "6267272b0deee3fb00d6f72b07b199c4", "packages": [ { "name": "automattic/jetpack-autoloader", @@ -628,16 +628,16 @@ }, { "name": "woocommerce/woocommerce-blocks", - "version": "9.8.3", + "version": "9.8.4", "source": { "type": "git", "url": "https://github.com/woocommerce/woocommerce-blocks.git", - "reference": "9431e10310a9dc89d844bb9d480adab64297b130" + "reference": "bdb2e6ab2288f980a7fa75fa46dc846fd0c6b31f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/woocommerce/woocommerce-blocks/zipball/9431e10310a9dc89d844bb9d480adab64297b130", - "reference": "9431e10310a9dc89d844bb9d480adab64297b130", + "url": "https://api.github.com/repos/woocommerce/woocommerce-blocks/zipball/bdb2e6ab2288f980a7fa75fa46dc846fd0c6b31f", + "reference": "bdb2e6ab2288f980a7fa75fa46dc846fd0c6b31f", "shasum": "" }, "require": { @@ -683,9 +683,9 @@ ], "support": { "issues": "https://github.com/woocommerce/woocommerce-blocks/issues", - "source": "https://github.com/woocommerce/woocommerce-blocks/tree/v9.8.3" + "source": "https://github.com/woocommerce/woocommerce-blocks/tree/v9.8.4" }, - "time": "2023-03-28T14:44:30+00:00" + "time": "2023-03-29T12:01:19+00:00" } ], "packages-dev": [ From 9892f7af28c3a411da8da4346c7689e908ffffd5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 29 Mar 2023 16:11:24 +0200 Subject: [PATCH 094/466] Delete changelog files based on PR 37477 (#37489) Delete changelog files for 37477 Co-authored-by: WooCommerce Bot --- plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.3 | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.3 diff --git a/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.3 b/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.3 deleted file mode 100644 index 974d6e765fd..00000000000 --- a/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.3 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Update WooCommerce Blocks to 9.8.3 From 5b234b92e1045ddd241900e5cc441d1b0440f359 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 29 Mar 2023 16:12:06 +0200 Subject: [PATCH 095/466] Delete changelog files based on PR 37492 (#37494) Delete changelog files for 37492 Co-authored-by: WooCommerce Bot --- plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.4 | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.4 diff --git a/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.4 b/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.4 deleted file mode 100644 index 2cb3821d89a..00000000000 --- a/plugins/woocommerce/changelog/update-woocommerce-blocks-9.8.4 +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: update - -Update WooCommerce Blocks to 9.8.4 From 6f5b7fd359acabd87d5dff469fa285f19ce2d33a Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Thu, 30 Mar 2023 09:13:09 +0800 Subject: [PATCH 096/466] Fix rest api filter to allow any strings in replacement (#37468) * Fix beta tester api filter replacement value * Add changelog * Fix lint * Add doc comments --- .../api/rest-api-filters/rest-api-filters.php | 208 ++++++++++-------- .../changelog/fix-rest-api-filter | 4 + 2 files changed, 123 insertions(+), 89 deletions(-) create mode 100644 plugins/woocommerce-beta-tester/changelog/fix-rest-api-filter diff --git a/plugins/woocommerce-beta-tester/api/rest-api-filters/rest-api-filters.php b/plugins/woocommerce-beta-tester/api/rest-api-filters/rest-api-filters.php index 19b06c9a722..c2cefb6158e 100644 --- a/plugins/woocommerce-beta-tester/api/rest-api-filters/rest-api-filters.php +++ b/plugins/woocommerce-beta-tester/api/rest-api-filters/rest-api-filters.php @@ -1,109 +1,139 @@ 'POST', - 'args' => array( - 'endpoint' => array( - 'description' => 'Rest API endpoint.', - 'type' => 'string', - 'required' => true, - 'sanitize_callback' => 'sanitize_text_field', - ), - 'dot_notation' => array( - 'description' => 'Dot notation of the target field.', - 'type' => 'string', - 'required' => true, - 'sanitize_callback' => 'sanitize_text_field', - ), - 'replacement' => array( - 'description' => 'Replacement value for the target field.', - 'type' => 'string', - 'required' => true, - 'sanitize_callback' => 'sanitize_text_field', - ), - ), - ) + '/rest-api-filters', + array( WCA_Test_Helper_Rest_Api_Filters::class, 'create' ), + array( + 'methods' => 'POST', + 'args' => array( + 'endpoint' => array( + 'description' => 'Rest API endpoint.', + 'type' => 'string', + 'required' => true, + 'sanitize_callback' => 'sanitize_text_field', + ), + 'dot_notation' => array( + 'description' => 'Dot notation of the target field.', + 'type' => 'string', + 'required' => true, + 'sanitize_callback' => 'sanitize_text_field', + ), + 'replacement' => array( + 'description' => 'Replacement value for the target field.', + 'type' => 'string', + 'required' => true, + 'sanitize_callback' => 'sanitize_text_field', + ), + ), + ) ); register_woocommerce_admin_test_helper_rest_route( - '/rest-api-filters', - [ WCA_Test_Helper_Rest_Api_Filters::class, 'delete' ], - array( - 'methods' => 'DELETE', - 'args' => array( - 'index' => array( - 'description' => 'Rest API endpoint.', - 'type' => 'integer', - 'required' => true, - ), - ), - ) + '/rest-api-filters', + array( WCA_Test_Helper_Rest_Api_Filters::class, 'delete' ), + array( + 'methods' => 'DELETE', + 'args' => array( + 'index' => array( + 'description' => 'Rest API endpoint.', + 'type' => 'integer', + 'required' => true, + ), + ), + ) ); register_woocommerce_admin_test_helper_rest_route( - '/rest-api-filters/(?P\d+)/toggle', - [ WCA_Test_Helper_Rest_Api_Filters::class, 'toggle' ], - array( - 'methods' => 'POST', - ) + '/rest-api-filters/(?P\d+)/toggle', + array( WCA_Test_Helper_Rest_Api_Filters::class, 'toggle' ), + array( + 'methods' => 'POST', + ) ); class WCA_Test_Helper_Rest_Api_Filters { - const WC_ADMIN_TEST_HELPER_REST_API_FILTER_OPTION = 'wc-admin-test-helper-rest-api-filters'; + const WC_ADMIN_TEST_HELPER_REST_API_FILTER_OPTION = 'wc-admin-test-helper-rest-api-filters'; - public static function create( $request ) { - $endpoint = $request->get_param('endpoint'); - $dot_notation = $request->get_param('dot_notation'); - $replacement = $request->get_param('replacement'); + /** + * Create a filter. + * + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response + */ + public static function create( $request ) { + $endpoint = $request->get_param( 'endpoint' ); + $dot_notation = $request->get_param( 'dot_notation' ); + $replacement = $request->get_param( 'replacement' ); - self::update( - function ( $filters ) use ( - $endpoint, - $dot_notation, - $replacement - ) { - $filters[] = array( - 'endpoint' => $endpoint, - 'dot_notation' => $dot_notation, - 'replacement' => filter_var( $replacement, FILTER_VALIDATE_BOOLEAN ), - 'enabled' => true, - ); - return $filters; - } - ); - return new WP_REST_RESPONSE(null, 204); - } + if ( 'false' === $replacement ) { + $replacement = false; + } elseif ( 'true' === $replacement ) { + $replacement = true; + } - public static function update( callable $callback ) { - $filters = get_option(self::WC_ADMIN_TEST_HELPER_REST_API_FILTER_OPTION, array()); - $filters = $callback( $filters ); - return update_option(self::WC_ADMIN_TEST_HELPER_REST_API_FILTER_OPTION, $filters); - } + self::update( + function ( $filters ) use ( + $endpoint, + $dot_notation, + $replacement + ) { + $filters[] = array( + 'endpoint' => $endpoint, + 'dot_notation' => $dot_notation, + 'replacement' => $replacement, + 'enabled' => true, + ); + return $filters; + } + ); + return new WP_REST_RESPONSE( null, 204 ); + } - public static function delete( $request ) { - self::update( - function ( $filters ) use ( $request ) { - array_splice($filters, $request->get_param('index'), 1); - return $filters; - } - ); + /** + * Update the filters. + * + * @param callable $callback Callback to update the filters. + * @return bool + */ + public static function update( callable $callback ) { + $filters = get_option( self::WC_ADMIN_TEST_HELPER_REST_API_FILTER_OPTION, array() ); + $filters = $callback( $filters ); + return update_option( self::WC_ADMIN_TEST_HELPER_REST_API_FILTER_OPTION, $filters ); + } - return new WP_REST_RESPONSE(null, 204); - } + /** + * Delete a filter. + * + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response + */ + public static function delete( $request ) { + self::update( + function ( $filters ) use ( $request ) { + array_splice( $filters, $request->get_param( 'index' ), 1 ); + return $filters; + } + ); - public static function toggle( $request ) { - self::update( - function ( $filters ) use ( $request ) { - $index = $request->get_param('index'); - $filters[$index]['enabled'] = !$filters[$index]['enabled']; - return $filters; - } - ); - return new WP_REST_RESPONSE(null, 204); - } -} \ No newline at end of file + return new WP_REST_RESPONSE( null, 204 ); + } + + /** + * Toggle a filter on or off. + * + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response + */ + public static function toggle( $request ) { + self::update( + function ( $filters ) use ( $request ) { + $index = $request->get_param( 'index' ); + $filters[ $index ]['enabled'] = ! $filters[ $index ]['enabled']; + return $filters; + } + ); + return new WP_REST_RESPONSE( null, 204 ); + } +} diff --git a/plugins/woocommerce-beta-tester/changelog/fix-rest-api-filter b/plugins/woocommerce-beta-tester/changelog/fix-rest-api-filter new file mode 100644 index 00000000000..91c2f4fd978 --- /dev/null +++ b/plugins/woocommerce-beta-tester/changelog/fix-rest-api-filter @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix rest api filter to allow any strings in replacement From 51af048c2848ed5ea534c2bc5692ba14e9410202 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Thu, 30 Mar 2023 09:15:17 +0800 Subject: [PATCH 097/466] Update payment gateway recommendation priority (#37442) * Update payment gateway recommendation priority * Reformat * Fix lint * Add changelog --- .../changelog/update-payment-order | 4 + .../DefaultPaymentGateways.php | 271 +++++++++++++++--- 2 files changed, 236 insertions(+), 39 deletions(-) create mode 100644 plugins/woocommerce/changelog/update-payment-order diff --git a/plugins/woocommerce/changelog/update-payment-order b/plugins/woocommerce/changelog/update-payment-order new file mode 100644 index 00000000000..dedd63eacb9 --- /dev/null +++ b/plugins/woocommerce/changelog/update-payment-order @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update payment gateway recommendation priority diff --git a/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/DefaultPaymentGateways.php b/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/DefaultPaymentGateways.php index 0f77f660c24..fbe2de7ab01 100644 --- a/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/DefaultPaymentGateways.php +++ b/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/DefaultPaymentGateways.php @@ -11,37 +11,6 @@ defined( 'ABSPATH' ) || exit; * Default Payment Gateways */ class DefaultPaymentGateways { - /** - * Priority is used to determine which payment gateway to recommend first. - * The lower the number, the higher the priority. - * - * @var array - */ - private static $recommendation_priority = array( - 'woocommerce_payments' => 1, - 'woocommerce_payments:with-in-person-payments' => 1, - 'woocommerce_payments:without-in-person-payments' => 1, - 'stripe' => 2, - 'woo-mercado-pago-custom' => 3, - // PayPal Payments. - 'ppcp-gateway' => 4, - 'mollie_wc_gateway_banktransfer' => 5, - 'razorpay' => 5, - 'payfast' => 5, - 'payubiz' => 6, - 'square_credit_card' => 6, - 'klarna_payments' => 6, - // Klarna Checkout. - 'kco' => 6, - 'paystack' => 6, - 'eway' => 7, - 'amazon_payments_advanced' => 7, - 'affirm' => 8, - 'afterpay' => 9, - 'zipmoney' => 10, - 'payoneer-checkout' => 11, - ); - /** * Get default specs. * @@ -832,8 +801,10 @@ class DefaultPaymentGateways { ), ); + $base_location = wc_get_base_location(); + $country = $base_location['country']; foreach ( $payment_gateways as $index => $payment_gateway ) { - $payment_gateways[ $index ]['recommendation_priority'] = self::get_recommendation_priority( $payment_gateway['id'] ); + $payment_gateways[ $index ]['recommendation_priority'] = self::get_recommendation_priority( $payment_gateway['id'], $country ); } return $payment_gateways; @@ -935,16 +906,238 @@ class DefaultPaymentGateways { } /** - * Get recommendation priority for a given payment gateway by id. - * If no priority is set or the id is not found, return null. + * Get recommendation priority for a given payment gateway by id and country. + * If country is not supported, return null. * - * @param string $id Payment gateway id. - * @return int Priority. + * @param string $gateway_id Payment gateway id. + * @param string $country_code Store country code. + * @return int|null Priority. Priority is 0-indexed, so 0 is the highest priority. */ - private static function get_recommendation_priority( $id ) { - if ( ! $id || ! array_key_exists( $id, self::$recommendation_priority ) ) { + private static function get_recommendation_priority( $gateway_id, $country_code ) { + $recommendation_priority_map = array( + 'US' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'square_credit_card', + 'amazon_payments_advanced', + 'affirm', + 'afterpay', + 'klarna_payments', + 'zipmoney', + ], + 'CA' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'square_credit_card', + 'affirm', + 'afterpay', + 'klarna_payments', + ], + 'AT' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'mollie_wc_gateway_banktransfer', + 'klarna_payments', + 'amazon_payments_advanced', + ], + 'BE' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'mollie_wc_gateway_banktransfer', + 'klarna_payments', + 'amazon_payments_advanced', + ], + 'BG' => [ 'stripe', 'ppcp-gateway' ], + 'HR' => [ 'ppcp-gateway' ], + 'CH' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'mollie_wc_gateway_banktransfer', + 'klarna_payments', + ], + 'CY' => [ 'stripe', 'ppcp-gateway', 'amazon_payments_advanced' ], + 'CZ' => [ 'stripe', 'ppcp-gateway' ], + 'DK' => [ + 'stripe', + 'ppcp-gateway', + 'klarna_payments', + 'amazon_payments_advanced', + ], + 'EE' => [ 'stripe', 'ppcp-gateway' ], + 'ES' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'mollie_wc_gateway_banktransfer', + 'square_credit_card', + 'klarna_payments', + 'amazon_payments_advanced', + ], + 'FI' => [ + 'stripe', + 'ppcp-gateway', + 'mollie_wc_gateway_banktransfer', + 'kco', + 'klarna_payments', + ], + 'FR' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'mollie_wc_gateway_banktransfer', + 'square_credit_card', + 'klarna_payments', + 'amazon_payments_advanced', + ], + 'DE' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'mollie_wc_gateway_banktransfer', + 'klarna_payments', + 'amazon_payments_advanced', + ], + 'GB' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'mollie_wc_gateway_banktransfer', + 'square_credit_card', + 'klarna_payments', + 'amazon_payments_advanced', + ], + 'GR' => [ 'stripe', 'ppcp-gateway' ], + 'HU' => [ 'stripe', 'ppcp-gateway', 'amazon_payments_advanced' ], + 'IE' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'square_credit_card', + 'amazon_payments_advanced', + ], + 'IT' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'mollie_wc_gateway_banktransfer', + 'klarna_payments', + 'amazon_payments_advanced', + ], + 'LV' => [ 'stripe', 'ppcp-gateway' ], + 'LT' => [ 'stripe', 'ppcp-gateway' ], + 'LU' => [ 'stripe', 'ppcp-gateway', 'amazon_payments_advanced' ], + 'MT' => [ 'stripe', 'ppcp-gateway' ], + 'NL' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'mollie_wc_gateway_banktransfer', + 'klarna_payments', + 'amazon_payments_advanced', + ], + 'NO' => [ 'stripe', 'ppcp-gateway', 'kco', 'klarna_payments' ], + 'PL' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'mollie_wc_gateway_banktransfer', + 'klarna_payments', + ], + 'PT' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'amazon_payments_advanced', + ], + 'RO' => [ 'stripe', 'ppcp-gateway' ], + 'SK' => [ 'stripe', 'ppcp-gateway' ], + 'SL' => [ 'stripe', 'ppcp-gateway', 'amazon_payments_advanced' ], + 'SE' => [ + 'stripe', + 'ppcp-gateway', + 'kco', + 'klarna_payments', + 'amazon_payments_advanced', + ], + 'MX' => [ + 'stripe', + 'woo-mercado-pago-custom', + 'ppcp-gateway', + 'klarna_payments', + ], + 'BR' => [ 'stripe', 'woo-mercado-pago-custom', 'ppcp-gateway' ], + 'AR' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ], + 'BO' => [], + 'CL' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ], + 'CO' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ], + 'EC' => [ 'ppcp-gateway' ], + 'FK' => [], + 'GF' => [], + 'GY' => [], + 'PY' => [], + 'PE' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ], + 'SR' => [], + 'UY' => [ 'woo-mercado-pago-custom', 'ppcp-gateway' ], + 'VE' => [ 'ppcp-gateway' ], + 'AU' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'square_credit_card', + 'eway', + 'afterpay', + 'klarna_payments', + 'zipmoney', + ], + 'NZ' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'eway', + 'klarna_payments', + 'zipmoney', + ], + 'HK' => [ + 'woocommerce_payments', + 'stripe', + 'ppcp-gateway', + 'eway', + 'payoneer-checkout', + ], + 'JP' => [ + 'stripe', + 'ppcp-gateway', + 'square_credit_card', + 'amazon_payments_advanced', + ], + 'SG' => [ 'woocommerce_payments', 'stripe', 'ppcp-gateway', 'eway' ], + 'CN' => [ 'ppcp-gateway', 'payoneer-checkout' ], + 'FJ' => [], + 'GU' => [], + 'ID' => [ 'stripe', 'ppcp-gateway' ], + 'IN' => [ 'stripe', 'razorpay', 'payubiz', 'ppcp-gateway' ], + 'ZA' => [ 'payfast', 'paystack', 'ppcp-gateway' ], + 'NG' => [ 'paystack', 'ppcp-gateway' ], + 'GH' => [ 'paystack', 'ppcp-gateway' ], + ); + + // If the country code is not in the list, return null. + if ( ! isset( $recommendation_priority_map[ $country_code ] ) ) { return null; } - return self::$recommendation_priority[ $id ]; + + $index = array_search( $gateway_id, $recommendation_priority_map[ $country_code ], true ); + + // If the gateway is not in the list, return the last index + 1. + if ( false === $index ) { + return count( $recommendation_priority_map[ $country_code ] ); + } + + return $index; } } From be9f09f2171432fb7341f3c1b8838b2637b32624 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Thu, 30 Mar 2023 09:51:44 +0800 Subject: [PATCH 098/466] Fix missing result prop in `wcadmin_install_plugin_error` track (#37466) * Fix missing result prop in install_plugin_error track * Add changelog --- plugins/woocommerce/changelog/fix-install-plugin-error-track | 4 ++++ plugins/woocommerce/src/Admin/PluginsHelper.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 plugins/woocommerce/changelog/fix-install-plugin-error-track diff --git a/plugins/woocommerce/changelog/fix-install-plugin-error-track b/plugins/woocommerce/changelog/fix-install-plugin-error-track new file mode 100644 index 00000000000..9be0defc8d8 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-install-plugin-error-track @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix missing result prop in wcadmin_install_plugin_error track diff --git a/plugins/woocommerce/src/Admin/PluginsHelper.php b/plugins/woocommerce/src/Admin/PluginsHelper.php index c333980947e..3f9e41a380a 100644 --- a/plugins/woocommerce/src/Admin/PluginsHelper.php +++ b/plugins/woocommerce/src/Admin/PluginsHelper.php @@ -234,7 +234,7 @@ class PluginsHelper { 'api_version' => $api->version, 'api_download_link' => $api->download_link, 'upgrader_skin_message' => implode( ',', $upgrader->skin->get_upgrade_messages() ), - 'result' => $result, + 'result' => is_wp_error( $result ) ? $result->get_error_message() : 'null', ); wc_admin_record_tracks_event( 'install_plugin_error', $properties ); From afc2344f47041000234130d82bd2a5714c1409ee Mon Sep 17 00:00:00 2001 From: Nima Karimi <73110514+nima-karimi@users.noreply.github.com> Date: Thu, 30 Mar 2023 14:13:57 +0100 Subject: [PATCH 099/466] Remove the MCM feature flag on update to WooCommerce 7.7 (#37454) * Remove the MCM feature flag on update to WooCommerce 7.7 This feature flag is no longer needed because the new marketing page will be the default on version 7.7 (pe2C5g-Ft-p2#comment-582). * Remove the marketing overview welcome option --- .../woocommerce/changelog/feature-remove-mcm-flag-option | 4 ++++ plugins/woocommerce/includes/class-wc-install.php | 3 +++ plugins/woocommerce/includes/wc-update-functions.php | 8 ++++++++ 3 files changed, 15 insertions(+) create mode 100644 plugins/woocommerce/changelog/feature-remove-mcm-flag-option diff --git a/plugins/woocommerce/changelog/feature-remove-mcm-flag-option b/plugins/woocommerce/changelog/feature-remove-mcm-flag-option new file mode 100644 index 00000000000..caa01e7e98b --- /dev/null +++ b/plugins/woocommerce/changelog/feature-remove-mcm-flag-option @@ -0,0 +1,4 @@ +Significance: minor +Type: tweak + +Remove the multichannel marketing feature flag from database since it's the default option now. diff --git a/plugins/woocommerce/includes/class-wc-install.php b/plugins/woocommerce/includes/class-wc-install.php index 33e21419f48..f2c1f4286bc 100644 --- a/plugins/woocommerce/includes/class-wc-install.php +++ b/plugins/woocommerce/includes/class-wc-install.php @@ -229,6 +229,9 @@ class WC_Install { 'wc_update_750_add_columns_to_order_stats_table', 'wc_update_750_disable_new_product_management_experience', ), + '7.7.0' => array( + 'wc_update_770_remove_multichannel_marketing_feature_options', + ), ); /** diff --git a/plugins/woocommerce/includes/wc-update-functions.php b/plugins/woocommerce/includes/wc-update-functions.php index 7c9020db0c7..d70a4233816 100644 --- a/plugins/woocommerce/includes/wc-update-functions.php +++ b/plugins/woocommerce/includes/wc-update-functions.php @@ -2581,3 +2581,11 @@ function wc_update_750_disable_new_product_management_experience() { update_option( 'woocommerce_new_product_management_enabled', 'no' ); } } + +/** + * Remove the multichannel marketing feature flag and options. This feature is now enabled by default. + */ +function wc_update_770_remove_multichannel_marketing_feature_options() { + delete_option( 'woocommerce_multichannel_marketing_enabled' ); + delete_option( 'woocommerce_marketing_overview_welcome_hidden' ); +} From d3229b97cbeb38f8a45b5d573a966147a6299a32 Mon Sep 17 00:00:00 2001 From: Ilyas Foo Date: Thu, 30 Mar 2023 23:36:12 +0800 Subject: [PATCH 100/466] Update mobile app modal image resolution (#37506) * Update image * Changelog --- .../illustrations/intro-devices-desktop.png | Bin 57751 -> 143961 bytes .../homescreen/mobile-app-modal/style.scss | 1 + .../fix-36142-mobile-app-image-resolution | 4 ++++ 3 files changed, 5 insertions(+) create mode 100644 plugins/woocommerce/changelog/fix-36142-mobile-app-image-resolution diff --git a/plugins/woocommerce-admin/client/homescreen/mobile-app-modal/illustrations/intro-devices-desktop.png b/plugins/woocommerce-admin/client/homescreen/mobile-app-modal/illustrations/intro-devices-desktop.png index 07932b5539c4b73cef1a732aa8e75d92b2307473..7911cb637a041b2b105094f6ca0c376dcde58ab5 100644 GIT binary patch literal 143961 zcmZ5mWmp?s(*^>;-GaNj6qjH{in|r3NQ+ZioZwR2J-EA;;_mJgcZ$37@w`93{gGVR zJv;ZvJ#!|z6Q-ξhB!0s{ksE-xpo4g&)Zfq?L@!N9;hsw!&Ay#K_+ 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 N002ovPDHLkV1k=n Date: Thu, 30 Mar 2023 11:22:07 -0700 Subject: [PATCH 101/466] Add product images block to product editor (#37455) * Add images block to the product editor * Allow html in section block descriptions * Add changelog entry * Add client changelog entry * Remove SVG related changes * Fix up lock file after rebase * Remove unused import * Fix up php lint errors * Move sanitize function to utils folder --- .../js/product-editor/changelog/add-37272 | 4 + packages/js/product-editor/package.json | 2 + .../src/components/editor/init-blocks.ts | 2 + .../src/components/images/block.json | 32 +++ .../src/components/images/edit.tsx | 184 ++++++++++++++++++ .../src/components/images/editor.scss | 21 ++ .../src/components/images/index.ts | 22 +++ .../src/components/section/edit.tsx | 12 +- packages/js/product-editor/src/style.scss | 1 + .../product-editor/src/utils/sanitize-html.ts | 13 ++ plugins/woocommerce/changelog/add-37272 | 4 + .../includes/class-wc-post-types.php | 20 ++ pnpm-lock.yaml | 99 +++++----- 13 files changed, 363 insertions(+), 53 deletions(-) create mode 100644 packages/js/product-editor/changelog/add-37272 create mode 100644 packages/js/product-editor/src/components/images/block.json create mode 100644 packages/js/product-editor/src/components/images/edit.tsx create mode 100644 packages/js/product-editor/src/components/images/editor.scss create mode 100644 packages/js/product-editor/src/components/images/index.ts create mode 100644 packages/js/product-editor/src/utils/sanitize-html.ts create mode 100644 plugins/woocommerce/changelog/add-37272 diff --git a/packages/js/product-editor/changelog/add-37272 b/packages/js/product-editor/changelog/add-37272 new file mode 100644 index 00000000000..5f3441d57be --- /dev/null +++ b/packages/js/product-editor/changelog/add-37272 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add images block to product editor diff --git a/packages/js/product-editor/package.json b/packages/js/product-editor/package.json index 37bdbadf283..c0e0cc448ba 100644 --- a/packages/js/product-editor/package.json +++ b/packages/js/product-editor/package.json @@ -58,6 +58,7 @@ "@wordpress/plugins": "wp-6.0", "@wordpress/url": "wp-6.0", "classnames": "^2.3.1", + "dompurify": "^2.3.6", "lodash": "^4.17.21", "prop-types": "^15.8.1", "react-router-dom": "^6.3.0" @@ -69,6 +70,7 @@ "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.1", "@testing-library/user-event": "^13.5.0", + "@types/dompurify": "^2.3.3", "@types/jest": "^27.4.1", "@types/react": "^17.0.2", "@types/testing-library__jest-dom": "^5.14.3", diff --git a/packages/js/product-editor/src/components/editor/init-blocks.ts b/packages/js/product-editor/src/components/editor/init-blocks.ts index 8b0977991bf..7a49334fb22 100644 --- a/packages/js/product-editor/src/components/editor/init-blocks.ts +++ b/packages/js/product-editor/src/components/editor/init-blocks.ts @@ -12,6 +12,7 @@ import { /** * Internal dependencies */ +import { init as initImages } from '../images'; import { init as initName } from '../details-name-block'; import { init as initSummary } from '../details-summary-block'; import { init as initSection } from '../section'; @@ -28,6 +29,7 @@ export const initBlocks = () => { // @ts-ignore An argument is allowed to specify which blocks to register. registerCoreBlocks( blocks ); + initImages(); initName(); initSummary(); initSection(); diff --git a/packages/js/product-editor/src/components/images/block.json b/packages/js/product-editor/src/components/images/block.json new file mode 100644 index 00000000000..28e4a958e13 --- /dev/null +++ b/packages/js/product-editor/src/components/images/block.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "woocommerce/product-images", + "title": "Product images", + "category": "widgets", + "description": "The product images.", + "keywords": [ "products", "image", "images", "gallery" ], + "textdomain": "default", + "attributes": { + "mediaId": { + "type": "number", + "__experimentalRole": "content" + }, + "images": { + "__experimentalRole": "content", + "type": "array", + "items": { + "type": "number" + }, + "default": [] + } + }, + "supports": { + "align": false, + "html": false, + "multiple": false, + "reusable": false, + "inserter": false, + "lock": false + } +} diff --git a/packages/js/product-editor/src/components/images/edit.tsx b/packages/js/product-editor/src/components/images/edit.tsx new file mode 100644 index 00000000000..3c668e74813 --- /dev/null +++ b/packages/js/product-editor/src/components/images/edit.tsx @@ -0,0 +1,184 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; +import { CardBody, DropZone } from '@wordpress/components'; +import classnames from 'classnames'; +import { createElement, useState } from '@wordpress/element'; +import { Icon, trash } from '@wordpress/icons'; +import { MediaItem } from '@wordpress/media-utils'; +import { + MediaUploader, + ImageGallery, + ImageGalleryItem, +} from '@woocommerce/components'; +import { recordEvent } from '@woocommerce/tracks'; +import { useBlockProps } from '@wordpress/block-editor'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore No types for this exist yet. +// eslint-disable-next-line @woocommerce/dependency-group +import { useEntityProp } from '@wordpress/core-data'; + +type Image = MediaItem & { + src: string; +}; + +export function Edit() { + const [ images, setImages ] = useEntityProp< MediaItem[] >( + 'postType', + 'product', + 'images' + ); + const [ isRemovingZoneVisible, setIsRemovingZoneVisible ] = + useState< boolean >( false ); + const [ isRemoving, setIsRemoving ] = useState< boolean >( false ); + const [ draggedImageId, setDraggedImageId ] = useState< number | null >( + null + ); + + const blockProps = useBlockProps( { + className: classnames( { + 'has-images': images.length > 0, + } ), + } ); + + const toggleRemoveZone = () => { + setIsRemovingZoneVisible( ! isRemovingZoneVisible ); + }; + + const orderImages = ( newOrder: JSX.Element[] ) => { + const orderedImages = newOrder.map( ( image ) => { + return images.find( + ( file ) => file.id === parseInt( image?.props?.id, 10 ) + ); + } ); + recordEvent( 'product_images_change_image_order_via_image_gallery' ); + setImages( orderedImages as MediaItem[] ); + }; + + const onFileUpload = ( files: MediaItem[] ) => { + if ( files[ 0 ].id ) { + recordEvent( 'product_images_add_via_file_upload_area' ); + setImages( [ ...images, ...files ] ); + } + }; + + return ( +
      +
      + { isRemovingZoneVisible ? ( + +
      + + + { __( 'Drop here to remove', 'woocommerce' ) } + + setIsRemoving( true ) } + onDrop={ () => setIsRemoving( true ) } + label={ __( + 'Drop here to remove', + 'woocommerce' + ) } + /> +
      +
      + ) : ( + + null } + onFileUploadChange={ onFileUpload } + onSelect={ ( files ) => { + const newImages = files.filter( + ( img: Image ) => + ! images.find( + ( image ) => image.id === img.id + ) + ); + if ( newImages.length > 0 ) { + recordEvent( + 'product_images_add_via_media_library' + ); + setImages( [ ...images, ...newImages ] ); + } + } } + onUpload={ ( files ) => { + if ( files[ 0 ].id ) { + recordEvent( + 'product_images_add_via_drag_and_drop_upload' + ); + setImages( [ ...images, ...files ] ); + } + } } + label={ '' } + /> + + ) } +
      + { + const { id: imageId, dataset } = + event.target as HTMLElement; + if ( imageId ) { + setDraggedImageId( parseInt( imageId, 10 ) ); + } else { + const index = dataset?.index; + if ( index ) { + setDraggedImageId( + images[ parseInt( index, 10 ) ]?.id + ); + } + } + toggleRemoveZone(); + } } + onDragEnd={ () => { + if ( isRemoving && draggedImageId ) { + recordEvent( + 'product_images_remove_image_button_click' + ); + setImages( + images.filter( + ( img ) => img.id !== draggedImageId + ) + ); + setIsRemoving( false ); + setDraggedImageId( null ); + } + toggleRemoveZone(); + } } + onOrderChange={ orderImages } + onReplace={ ( { replaceIndex, media } ) => { + if ( + images.find( ( img ) => media.id === img.id ) === + undefined + ) { + images[ replaceIndex ] = media as MediaItem; + recordEvent( + 'product_images_replace_image_button_click' + ); + setImages( images ); + } + } } + onSelectAsCover={ () => + recordEvent( + 'product_images_select_image_as_cover_button_click' + ) + } + > + { images.map( ( image ) => ( + + ) ) } + +
      + ); +} diff --git a/packages/js/product-editor/src/components/images/editor.scss b/packages/js/product-editor/src/components/images/editor.scss new file mode 100644 index 00000000000..29228aec4d6 --- /dev/null +++ b/packages/js/product-editor/src/components/images/editor.scss @@ -0,0 +1,21 @@ +.wp-block-woocommerce-product-images { + .components-card__body { + padding: 0 0 40px 0; + } + .woocommerce-media-uploader { + text-align: left; + } + .woocommerce-media-uploader__label { + display: none; + } + .woocommerce-sortable { + margin-top: 0; + padding: 0; + } + + &:not(.has-images) { + .woocommerce-sortable { + display: none; + } + } +} diff --git a/packages/js/product-editor/src/components/images/index.ts b/packages/js/product-editor/src/components/images/index.ts new file mode 100644 index 00000000000..59d35906a12 --- /dev/null +++ b/packages/js/product-editor/src/components/images/index.ts @@ -0,0 +1,22 @@ +/** + * Internal dependencies + */ +import { initBlock } from '../../utils'; +import metadata from './block.json'; +import { Edit } from './edit'; + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + example: {}, + edit: Edit, +}; + +export const init = () => + initBlock( { + name, + metadata: metadata as never, + settings, + } ); diff --git a/packages/js/product-editor/src/components/section/edit.tsx b/packages/js/product-editor/src/components/section/edit.tsx index 369055d4a0d..9e32b344529 100644 --- a/packages/js/product-editor/src/components/section/edit.tsx +++ b/packages/js/product-editor/src/components/section/edit.tsx @@ -2,14 +2,15 @@ * External dependencies */ import { createElement } from '@wordpress/element'; -import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; import type { BlockEditProps } from '@wordpress/blocks'; +import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; /** * Internal dependencies */ -import { SectionBlockAttributes } from './types'; import { BlockIcon } from '../block-icon'; +import { SectionBlockAttributes } from './types'; +import { sanitizeHTML } from '../../utils/sanitize-html'; export function Edit( { attributes, @@ -24,9 +25,10 @@ export function Edit( { { title } -

      - { description } -

      +

      ); diff --git a/packages/js/product-editor/src/style.scss b/packages/js/product-editor/src/style.scss index 955c5d3813d..a4d59bce446 100644 --- a/packages/js/product-editor/src/style.scss +++ b/packages/js/product-editor/src/style.scss @@ -4,6 +4,7 @@ @import 'components/details-categories-field/style.scss'; @import 'components/details-categories-field/create-category-modal.scss'; @import 'components/header/style.scss'; +@import 'components/images/editor.scss'; @import 'components/block-editor/style.scss'; @import 'components/section/style.scss'; @import 'components/tab/style.scss'; diff --git a/packages/js/product-editor/src/utils/sanitize-html.ts b/packages/js/product-editor/src/utils/sanitize-html.ts new file mode 100644 index 00000000000..fb47b6530a3 --- /dev/null +++ b/packages/js/product-editor/src/utils/sanitize-html.ts @@ -0,0 +1,13 @@ +/** + * External dependencies + */ +import { sanitize } from 'dompurify'; + +const ALLOWED_TAGS = [ 'a', 'b', 'em', 'i', 'strong', 'p', 'br' ]; +const ALLOWED_ATTR = [ 'target', 'href', 'rel', 'name', 'download' ]; + +export function sanitizeHTML( html: string ) { + return { + __html: sanitize( html, { ALLOWED_TAGS, ALLOWED_ATTR } ), + }; +} diff --git a/plugins/woocommerce/changelog/add-37272 b/plugins/woocommerce/changelog/add-37272 new file mode 100644 index 00000000000..b87251a28a6 --- /dev/null +++ b/plugins/woocommerce/changelog/add-37272 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add images block to product editor template diff --git a/plugins/woocommerce/includes/class-wc-post-types.php b/plugins/woocommerce/includes/class-wc-post-types.php index 3d630729817..2cf291c43a4 100644 --- a/plugins/woocommerce/includes/class-wc-post-types.php +++ b/plugins/woocommerce/includes/class-wc-post-types.php @@ -432,6 +432,26 @@ class WC_Post_Types { ), ), ), + array( + 'woocommerce/product-section', + array( + 'title' => __( 'Images', 'woocommerce' ), + 'description' => sprintf( + /* translators: %1$s: Images guide link opening tag. %2$s: Images guide link closing tag.*/ + __( 'Drag images, upload new ones or select files from your library. For best results, use JPEG files that are 1000 by 1000 pixels or larger. %1$sHow to prepare images?%2$s.', 'woocommerce' ), + '', + '' + ), + ), + array( + array( + 'woocommerce/product-images', + array( + 'images' => array(), + ), + ), + ), + ), ), ), array( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 173c95cb63b..ef8830eed25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1416,6 +1416,7 @@ importers: '@testing-library/react': ^12.1.3 '@testing-library/react-hooks': ^8.0.1 '@testing-library/user-event': ^13.5.0 + '@types/dompurify': ^2.3.3 '@types/jest': ^27.4.1 '@types/lodash': ^4.14.179 '@types/prop-types': ^15.7.4 @@ -1463,6 +1464,7 @@ importers: classnames: ^2.3.1 concurrently: ^7.0.0 css-loader: ^3.6.0 + dompurify: ^2.3.6 eslint: ^8.32.0 jest: ^27.5.1 jest-cli: ^27.5.1 @@ -1511,6 +1513,7 @@ importers: '@wordpress/plugins': 4.4.3_react@17.0.2 '@wordpress/url': 3.7.1 classnames: 2.3.1 + dompurify: 2.3.6 lodash: 4.17.21 prop-types: 15.8.1 react-router-dom: 6.3.0_sfoxds7t5ydpegc3knd667wn6m @@ -1521,6 +1524,7 @@ importers: '@testing-library/react': 12.1.4_sfoxds7t5ydpegc3knd667wn6m '@testing-library/react-hooks': 8.0.1_hiunvzosbwliizyirxfy6hjyim '@testing-library/user-event': 13.5.0_gzufz4q333be4gqfrvipwvqt6a + '@types/dompurify': 2.3.3 '@types/jest': 27.4.1 '@types/react': 17.0.50 '@types/testing-library__jest-dom': 5.14.3 @@ -8465,6 +8469,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.13.11 + dev: true /@babel/runtime/7.21.0: resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==} @@ -8777,7 +8782,7 @@ packages: optional: true dependencies: '@babel/core': 7.17.8 - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.21.0 '@emotion/babel-plugin': 11.10.5_@babel+core@7.17.8 '@emotion/cache': 11.10.5 '@emotion/serialize': 1.1.1 @@ -8801,7 +8806,7 @@ packages: optional: true dependencies: '@babel/core': 7.17.8 - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.21.0 '@emotion/babel-plugin': 11.10.5_@babel+core@7.17.8 '@emotion/cache': 11.10.5 '@emotion/serialize': 1.1.1 @@ -16616,21 +16621,21 @@ packages: '@emotion/styled': 11.8.1_6t3indjc5ssefvr44gr3wo2uqu '@emotion/utils': 1.0.0 '@use-gesture/react': 10.2.10_react@17.0.2 - '@wordpress/a11y': 3.28.0 + '@wordpress/a11y': 3.6.1 '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/date': 4.28.0 - '@wordpress/deprecated': 3.28.0 - '@wordpress/dom': 3.28.0 + '@wordpress/date': 4.6.1 + '@wordpress/deprecated': 3.6.1 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/escape-html': 2.28.0 - '@wordpress/hooks': 3.28.0 + '@wordpress/hooks': 3.6.1 '@wordpress/i18n': 4.6.1 '@wordpress/icons': 8.2.3 '@wordpress/is-shallow-equal': 4.28.0 - '@wordpress/keycodes': 3.28.0 - '@wordpress/primitives': 3.26.0 - '@wordpress/rich-text': 5.17.0_react@17.0.2 - '@wordpress/warning': 2.28.0 + '@wordpress/keycodes': 3.6.1 + '@wordpress/primitives': 3.4.1 + '@wordpress/rich-text': 5.4.2_react@17.0.2 + '@wordpress/warning': 2.6.1 classnames: 2.3.1 colord: 2.9.2 dom-scroll-into-view: 1.2.1 @@ -17096,11 +17101,11 @@ packages: '@babel/runtime': 7.21.0 '@types/lodash': 4.14.184 '@types/mousetrap': 1.6.9 - '@wordpress/deprecated': 3.28.0 - '@wordpress/dom': 3.28.0 + '@wordpress/deprecated': 3.6.1 + '@wordpress/dom': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/is-shallow-equal': 4.28.0 - '@wordpress/keycodes': 3.28.0 + '@wordpress/keycodes': 3.6.1 '@wordpress/priority-queue': 2.28.0 clipboard: 2.0.10 lodash: 4.17.21 @@ -17136,10 +17141,10 @@ packages: react: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/api-fetch': 6.25.0 + '@wordpress/api-fetch': 6.3.1 '@wordpress/blocks': 11.18.0_react@17.0.2 '@wordpress/data': 6.6.1_react@17.0.2 - '@wordpress/deprecated': 3.28.0 + '@wordpress/deprecated': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/html-entities': 3.6.1 '@wordpress/i18n': 4.6.1 @@ -17279,7 +17284,7 @@ packages: dependencies: '@babel/runtime': 7.21.0 '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/deprecated': 3.28.0 + '@wordpress/deprecated': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/is-shallow-equal': 4.28.0 '@wordpress/priority-queue': 2.28.0 @@ -17394,7 +17399,6 @@ packages: dependencies: '@babel/runtime': 7.21.0 '@wordpress/hooks': 3.6.1 - dev: false /@wordpress/dom-ready/3.28.0: resolution: {integrity: sha512-PFFAnuPUouV0uSDZN6G/B8yksybtxzS/6H53OZJEA3h3EsNCicKRMGSowkumvLwXA23HV0K2Kht6JuS+bDECzA==} @@ -17428,7 +17432,6 @@ packages: dependencies: '@babel/runtime': 7.21.0 lodash: 4.17.21 - dev: false /@wordpress/e2e-test-utils/3.0.0_ddjhsfu4aotkh3cuzmpsln6ywq: resolution: {integrity: sha512-XMdR8DeKyDQRF5jKeUlOzP4pTRtoJuOLsNZRLUFUvnrs9y/7/hH17VmPbWp3TJGvV/eGKzO4+D+wJTsP9nJmIw==} @@ -17495,28 +17498,28 @@ packages: react-dom: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/a11y': 3.28.0 - '@wordpress/api-fetch': 6.25.0 + '@wordpress/a11y': 3.6.1 + '@wordpress/api-fetch': 6.3.1 '@wordpress/block-editor': 8.6.0_eqi5qhcxfphl6j3pngzexvnehi '@wordpress/blocks': 11.18.0_react@17.0.2 '@wordpress/components': 19.8.5_eqi5qhcxfphl6j3pngzexvnehi '@wordpress/compose': 5.4.1_react@17.0.2 '@wordpress/core-data': 4.4.5_react@17.0.2 '@wordpress/data': 6.6.1_react@17.0.2 - '@wordpress/date': 4.28.0 - '@wordpress/deprecated': 3.28.0 + '@wordpress/date': 4.6.1 + '@wordpress/deprecated': 3.6.1 '@wordpress/element': 4.4.1 - '@wordpress/hooks': 3.28.0 + '@wordpress/hooks': 3.6.1 '@wordpress/html-entities': 3.6.1 '@wordpress/i18n': 4.6.1 '@wordpress/icons': 8.2.3 '@wordpress/keyboard-shortcuts': 3.4.1_react@17.0.2 - '@wordpress/keycodes': 3.28.0 + '@wordpress/keycodes': 3.6.1 '@wordpress/media-utils': 3.4.1 - '@wordpress/notices': 3.28.0_react@17.0.2 + '@wordpress/notices': 3.6.1_react@17.0.2 '@wordpress/preferences': 1.3.0_eqi5qhcxfphl6j3pngzexvnehi '@wordpress/reusable-blocks': 3.17.0_mtk4wljkd5jimhszw4p7nnxuzm - '@wordpress/rich-text': 5.17.0_react@17.0.2 + '@wordpress/rich-text': 5.4.2_react@17.0.2 '@wordpress/server-side-render': 3.17.0_mtk4wljkd5jimhszw4p7nnxuzm '@wordpress/url': 3.7.1 '@wordpress/wordcount': 3.28.0 @@ -17817,7 +17820,6 @@ packages: engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - dev: false /@wordpress/html-entities/3.28.0: resolution: {integrity: sha512-UAaU6au8UTrSkowkV33pE/EvdPov2mA9W51vh6t88KsJPzt4171EzIchGnQuzt04HuZLdLyWy2A+7JCOSbfhBA==} @@ -17876,7 +17878,7 @@ packages: hasBin: true dependencies: '@babel/runtime': 7.21.0 - '@wordpress/hooks': 3.28.0 + '@wordpress/hooks': 3.6.1 gettext-parser: 1.4.0 lodash: 4.17.21 memize: 1.1.0 @@ -17898,7 +17900,7 @@ packages: dependencies: '@babel/runtime': 7.21.0 '@wordpress/element': 4.4.1 - '@wordpress/primitives': 3.26.0 + '@wordpress/primitives': 3.4.1 dev: false /@wordpress/icons/8.4.0: @@ -17926,17 +17928,17 @@ packages: react-dom: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/a11y': 3.28.0 + '@wordpress/a11y': 3.6.1 '@wordpress/components': 19.8.5_eqi5qhcxfphl6j3pngzexvnehi '@wordpress/compose': 5.4.1_react@17.0.2 '@wordpress/data': 6.6.1_react@17.0.2 - '@wordpress/deprecated': 3.28.0 + '@wordpress/deprecated': 3.6.1 '@wordpress/element': 4.4.1 '@wordpress/i18n': 4.6.1 '@wordpress/icons': 8.2.3 '@wordpress/plugins': 4.4.3_react@17.0.2 '@wordpress/preferences': 1.3.0_eqi5qhcxfphl6j3pngzexvnehi - '@wordpress/viewport': 4.17.0_react@17.0.2 + '@wordpress/viewport': 4.4.1_react@17.0.2 classnames: 2.3.1 lodash: 4.17.21 react: 17.0.2 @@ -18120,7 +18122,7 @@ packages: '@babel/runtime': 7.21.0 '@wordpress/data': 6.6.1_react@17.0.2 '@wordpress/element': 4.4.1 - '@wordpress/keycodes': 3.28.0 + '@wordpress/keycodes': 3.6.1 lodash: 4.17.21 react: 17.0.2 rememo: 3.0.0 @@ -18157,14 +18159,13 @@ packages: '@babel/runtime': 7.21.0 '@wordpress/i18n': 4.6.1 lodash: 4.17.21 - dev: false /@wordpress/media-utils/3.4.1: resolution: {integrity: sha512-WNAaMqqw6Eqy61KTWBy1NlgXSZH82Cm2SPVbz0v6yhJ4ktJmSRFm7Fd4mTMFS/L7NKTxwo+DFqEHlTGKj3lyzQ==} engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - '@wordpress/api-fetch': 6.25.0 + '@wordpress/api-fetch': 6.3.1 '@wordpress/blob': 3.28.0 '@wordpress/element': 4.4.1 '@wordpress/i18n': 4.6.1 @@ -18221,7 +18222,7 @@ packages: '@babel/runtime': 7.21.0 '@wordpress/compose': 5.4.1_react@17.0.2 '@wordpress/element': 4.4.1 - '@wordpress/hooks': 3.28.0 + '@wordpress/hooks': 3.6.1 '@wordpress/icons': 8.2.3 lodash: 4.17.21 memize: 1.1.0 @@ -19635,8 +19636,8 @@ packages: peerDependencies: postcss: ^8.1.0 dependencies: - browserslist: 4.20.2 - caniuse-lite: 1.0.30001352 + browserslist: 4.21.4 + caniuse-lite: 1.0.30001418 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -21141,6 +21142,7 @@ packages: escalade: 3.1.1 node-releases: 2.0.6 picocolors: 1.0.0 + dev: true /browserslist/4.20.4: resolution: {integrity: sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==} @@ -24519,9 +24521,9 @@ packages: has: 1.0.3 is-core-module: 2.8.0 is-glob: 4.0.3 - minimatch: 3.0.4 + minimatch: 3.1.2 object.values: 1.1.5 - resolve: 1.20.0 + resolve: 1.22.1 tsconfig-paths: 3.14.0 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -24550,9 +24552,9 @@ packages: has: 1.0.3 is-core-module: 2.8.0 is-glob: 4.0.3 - minimatch: 3.0.4 + minimatch: 3.1.2 object.values: 1.1.5 - resolve: 1.20.0 + resolve: 1.22.1 tsconfig-paths: 3.14.0 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -33112,6 +33114,7 @@ packages: resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} dependencies: brace-expansion: 1.1.11 + dev: true /minimatch/3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -36586,7 +36589,7 @@ packages: is-touch-device: 1.0.1 lodash: 4.17.21 moment: 2.29.4 - object.assign: 4.1.4 + object.assign: 4.1.2 object.values: 1.1.5 prop-types: 15.8.1 raf: 3.4.1 @@ -36596,7 +36599,7 @@ packages: react-outside-click-handler: 1.3.0_sfoxds7t5ydpegc3knd667wn6m react-portal: 4.2.1_sfoxds7t5ydpegc3knd667wn6m react-with-direction: 1.4.0_sfoxds7t5ydpegc3knd667wn6m - react-with-styles: 4.2.0_tzgwoaxjvs23ve2qhnwxwqxt3e + react-with-styles: 4.2.0_ijumbsfrpvhmwfiiw5c3tpdyzq react-with-styles-interface-css: 6.0.0_eivcclzbvdxqy5cnlnup5iddhy dev: false @@ -37212,7 +37215,7 @@ packages: '@babel/runtime': 7.21.0 array.prototype.flat: 1.2.5 global-cache: 1.2.1 - react-with-styles: 4.2.0_tzgwoaxjvs23ve2qhnwxwqxt3e + react-with-styles: 4.2.0_ijumbsfrpvhmwfiiw5c3tpdyzq dev: false /react-with-styles-interface-css/6.0.0_u5gvwsivijvqc4cln26hqg7igq: @@ -37240,14 +37243,14 @@ packages: react-with-direction: 1.4.0_sfoxds7t5ydpegc3knd667wn6m dev: false - /react-with-styles/4.2.0_tzgwoaxjvs23ve2qhnwxwqxt3e: + /react-with-styles/4.2.0_ijumbsfrpvhmwfiiw5c3tpdyzq: resolution: {integrity: sha512-tZCTY27KriRNhwHIbg1NkSdTTOSfXDg6Z7s+Q37mtz0Ym7Sc7IOr3PzVt4qJhJMW6Nkvfi3g34FuhtiGAJCBQA==} peerDependencies: '@babel/runtime': ^7.0.0 react: '>=0.14' react-with-direction: ^1.3.1 dependencies: - '@babel/runtime': 7.19.0 + '@babel/runtime': 7.21.0 airbnb-prop-types: 2.16.0_react@17.0.2 hoist-non-react-statics: 3.3.2 object.assign: 4.1.4 From 658034f64e2fad22fad3b7bef888be34eed31acb Mon Sep 17 00:00:00 2001 From: Moon Date: Thu, 30 Mar 2023 12:07:58 -0700 Subject: [PATCH 102/466] Migrate steps/location to TS (#37257) * Migrate steps location to TS * Add description for ts-expect-error --- .../fills/steps/{location.js => location.tsx} | 76 +++++++++++++------ .../update-37188-migrate-location-step-to-ts | 4 + 2 files changed, 58 insertions(+), 22 deletions(-) rename plugins/woocommerce-admin/client/tasks/fills/steps/{location.js => location.tsx} (57%) create mode 100644 plugins/woocommerce/changelog/update-37188-migrate-location-step-to-ts diff --git a/plugins/woocommerce-admin/client/tasks/fills/steps/location.js b/plugins/woocommerce-admin/client/tasks/fills/steps/location.tsx similarity index 57% rename from plugins/woocommerce-admin/client/tasks/fills/steps/location.js rename to plugins/woocommerce-admin/client/tasks/fills/steps/location.tsx index a4cce788a99..da75cd2c374 100644 --- a/plugins/woocommerce-admin/client/tasks/fills/steps/location.js +++ b/plugins/woocommerce-admin/client/tasks/fills/steps/location.tsx @@ -5,9 +5,9 @@ import { __ } from '@wordpress/i18n'; import { Button } from '@wordpress/components'; import { COUNTRIES_STORE_NAME } from '@woocommerce/data'; import { Fragment } from '@wordpress/element'; -import { Form, Spinner } from '@woocommerce/components'; +import { Form, FormContext, Spinner } from '@woocommerce/components'; import { useSelect } from '@wordpress/data'; - +import { Status, Options } from 'wordpress__notices'; /** * Internal dependencies */ @@ -16,6 +16,42 @@ import { getStoreAddressValidator, } from '../../../dashboard/components/settings/general/store-address'; +type FormValues = { + addressLine1: string; + addressLine2: string; + countryState: string; + city: string; + postCode: string; +}; + +type StoreLocationProps = { + onComplete: ( values: FormValues ) => void; + createNotice: ( + status: Status | undefined, + content: string, + options?: Partial< Options > + ) => void; + isSettingsError: boolean; + isSettingsRequesting: boolean; + buttonText?: string; + updateAndPersistSettingsForGroup: ( + group: string, + data: { + [ key: string ]: unknown; + } & { + general?: { + [ key: string ]: string; + }; + tax?: { + [ key: string ]: string; + }; + } + ) => void; + settings?: { + [ key: string ]: string; + }; +}; + const StoreLocation = ( { onComplete, createNotice, @@ -24,8 +60,8 @@ const StoreLocation = ( { updateAndPersistSettingsForGroup, settings, buttonText = __( 'Continue', 'woocommerce' ), -} ) => { - const { getLocale, hasFinishedResolution } = useSelect( ( select ) => { +}: StoreLocationProps ) => { + const { hasFinishedResolution } = useSelect( ( select ) => { const countryStore = select( COUNTRIES_STORE_NAME ); countryStore.getCountries(); return { @@ -36,7 +72,7 @@ const StoreLocation = ( { countryStore.hasFinishedResolution( 'getCountries' ), }; } ); - const onSubmit = async ( values ) => { + const onSubmit = async ( values: FormValues ) => { await updateAndPersistSettingsForGroup( 'general', { general: { ...settings, @@ -62,26 +98,17 @@ const StoreLocation = ( { }; const getInitialValues = () => { - const { - woocommerce_store_address: storeAddress, - woocommerce_store_address_2: storeAddress2, - woocommerce_store_city: storeCity, - woocommerce_default_country: defaultCountry, - woocommerce_store_postcode: storePostcode, - } = settings; - return { - addressLine1: storeAddress || '', - addressLine2: storeAddress2 || '', - city: storeCity || '', - countryState: defaultCountry || '', - postCode: storePostcode || '', + addressLine1: settings?.woocommerce_store_address || '', + addressLine2: settings?.woocommerce_store_address_2 || '', + city: settings?.woocommerce_store_city || '', + countryState: settings?.woocommerce_default_country || '', + postCode: settings?.woocommerce_store_postcode || '', }; }; - const validate = ( values ) => { - const locale = getLocale( values.countryState ); - const validator = getStoreAddressValidator( locale ); + const validate = ( values: FormValues ) => { + const validator = getStoreAddressValidator(); return validator( values ); }; @@ -95,9 +122,14 @@ const StoreLocation = ( { onSubmit={ onSubmit } validate={ validate } > - { ( { getInputProps, handleSubmit, setValue } ) => ( + { ( { + getInputProps, + handleSubmit, + setValue, + }: FormContext< FormValues > ) => ( diff --git a/plugins/woocommerce/changelog/update-37188-migrate-location-step-to-ts b/plugins/woocommerce/changelog/update-37188-migrate-location-step-to-ts new file mode 100644 index 00000000000..c0223de6799 --- /dev/null +++ b/plugins/woocommerce/changelog/update-37188-migrate-location-step-to-ts @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Migrate steps location task to TS From deb1cde4480622e398dedafea6ba8565c59d67ca Mon Sep 17 00:00:00 2001 From: Nathan Silveira Date: Thu, 30 Mar 2023 16:41:20 -0300 Subject: [PATCH 103/466] Add listener to parent to avoid event being overwritten when DOM is replaced (#37431) --- .../woocommerce/changelog/fix-disable-save-btn | 5 +++++ .../client/legacy/js/admin/meta-boxes.js | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 plugins/woocommerce/changelog/fix-disable-save-btn diff --git a/plugins/woocommerce/changelog/fix-disable-save-btn b/plugins/woocommerce/changelog/fix-disable-save-btn new file mode 100644 index 00000000000..37f582bd74e --- /dev/null +++ b/plugins/woocommerce/changelog/fix-disable-save-btn @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: Attach event to parent node to avoid it being overwritten when DOM changes + + diff --git a/plugins/woocommerce/client/legacy/js/admin/meta-boxes.js b/plugins/woocommerce/client/legacy/js/admin/meta-boxes.js index 5e58c4a902a..bffcde118e8 100644 --- a/plugins/woocommerce/client/legacy/js/admin/meta-boxes.js +++ b/plugins/woocommerce/client/legacy/js/admin/meta-boxes.js @@ -30,13 +30,16 @@ jQuery( function ( $ ) { * Function to maybe disable the save button. */ jQuery.maybe_disable_save_button = function () { - var $tab = $( '.product_attributes' ); - var $save_button = $( 'button.save_attributes' ); + var $tab; + var $save_button; if ( $( '.woocommerce_variation_new_attribute_data' ).is( ':visible' ) ) { $tab = $( '.woocommerce_variation_new_attribute_data' ); $save_button = $( 'button.create-variations' ); + } else { + var $tab = $( '.product_attributes' ); + var $save_button = $( 'button.save_attributes' ); } var attributes_and_variations_data = $tab.find( @@ -153,15 +156,14 @@ jQuery( function ( $ ) { $( this ).find( '.wc-metabox-content' ).hide(); } ); - $( '.product_attributes, .woocommerce_variation_new_attribute_data' ).on( - 'keyup', - 'input, textarea', - jQuery.maybe_disable_save_button - ); - $( '#product_attributes' ).on( 'change', 'select.attribute_values', jQuery.maybe_disable_save_button ); + $( '#product_attributes, #variable_product_options' ).on( + 'keyup', + 'input, textarea', + jQuery.maybe_disable_save_button + ); } ); From e553759ab6e79547836de91356e6a6d35b9d6b89 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Fri, 31 Mar 2023 18:43:16 +0800 Subject: [PATCH 104/466] Fix `FormContext` type error in client/tasks/fills/steps/location.tsx (#37532) * Fix FormContext type error * Add changelog --- .../woocommerce-admin/client/tasks/fills/steps/location.tsx | 4 ++-- plugins/woocommerce/changelog/fix-location-type-error | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 plugins/woocommerce/changelog/fix-location-type-error diff --git a/plugins/woocommerce-admin/client/tasks/fills/steps/location.tsx b/plugins/woocommerce-admin/client/tasks/fills/steps/location.tsx index da75cd2c374..670f5a5016d 100644 --- a/plugins/woocommerce-admin/client/tasks/fills/steps/location.tsx +++ b/plugins/woocommerce-admin/client/tasks/fills/steps/location.tsx @@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n'; import { Button } from '@wordpress/components'; import { COUNTRIES_STORE_NAME } from '@woocommerce/data'; import { Fragment } from '@wordpress/element'; -import { Form, FormContext, Spinner } from '@woocommerce/components'; +import { Form, FormContextType, Spinner } from '@woocommerce/components'; import { useSelect } from '@wordpress/data'; import { Status, Options } from 'wordpress__notices'; /** @@ -126,7 +126,7 @@ const StoreLocation = ( { getInputProps, handleSubmit, setValue, - }: FormContext< FormValues > ) => ( + }: FormContextType< FormValues > ) => ( Date: Fri, 31 Mar 2023 10:36:40 -0700 Subject: [PATCH 105/466] Add block related assets entry points to product editor build (#37318) * Add editorStyle properties to blocks * Rename style.scss files to editor.scss * Get block entry points for block related assets * Copy block assets to core assets build folder * Remove unusable dependency * Add changelog entries * Fix up RTL style builds for block assets * Update copy-webpack-plugin dependency and lock file * Fix up lock file after rebase * Fix order of webpack rtl stylesheet builds to prevent additional stylsheets * Fix up lock file after rebase * Fix tsconfig --- .../changelog/add-37241-blocks-build | 4 + .../config/block-entry-points.js | 145 +++++ packages/js/product-editor/package.json | 1 + .../components/details-name-block/block.json | 3 +- .../src/components/pricing-block/block.json | 3 +- .../src/components/section/block.json | 3 +- .../section/{style.scss => editor.scss} | 0 .../tab/{style.scss => editor.scss} | 0 packages/js/product-editor/src/style.scss | 4 +- packages/js/product-editor/tsconfig-cjs.json | 3 +- packages/js/product-editor/webpack.config.js | 56 +- plugins/woocommerce-admin/webpack.config.js | 12 + .../changelog/add-37241-blocks-build | 4 + pnpm-lock.yaml | 543 ++++++------------ 14 files changed, 392 insertions(+), 389 deletions(-) create mode 100644 packages/js/product-editor/changelog/add-37241-blocks-build create mode 100644 packages/js/product-editor/config/block-entry-points.js rename packages/js/product-editor/src/components/section/{style.scss => editor.scss} (100%) rename packages/js/product-editor/src/components/tab/{style.scss => editor.scss} (100%) create mode 100644 plugins/woocommerce/changelog/add-37241-blocks-build diff --git a/packages/js/product-editor/changelog/add-37241-blocks-build b/packages/js/product-editor/changelog/add-37241-blocks-build new file mode 100644 index 00000000000..5faad0551c3 --- /dev/null +++ b/packages/js/product-editor/changelog/add-37241-blocks-build @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add block related assets entry points to build diff --git a/packages/js/product-editor/config/block-entry-points.js b/packages/js/product-editor/config/block-entry-points.js new file mode 100644 index 00000000000..c23f6b3b3d1 --- /dev/null +++ b/packages/js/product-editor/config/block-entry-points.js @@ -0,0 +1,145 @@ +/** + * External dependencies + */ +const fs = require( 'fs' ); +const path = require( 'path' ); +const { sync: glob } = require( 'fast-glob' ); + +const srcDir = path.resolve( process.cwd(), 'src' ); +const blocksBuildDir = '/build/blocks'; + +/** + * Get all the block meta data files in the src directory. + * + * @return {string[]} Block file paths. + */ +const getBlockMetaDataFiles = () => { + return glob( `${ srcDir.replace( /\\/g, '/' ) }/**/block.json`, { + absolute: true, + } ); +}; + +/** + * Get the block meta data from a block.json file. + * + * @param {string} filePath File path to block.json file. + * @return {Object} Block meta data. + */ +const getBlockMetaData = ( filePath ) => { + return JSON.parse( fs.readFileSync( filePath ) ); +}; + +/** + * Get the block file assets with raw file paths. + * + * @param {Object} blockMetaData + * @return {string[]} Asset file paths. + */ +const getBlockFileAssets = ( blockMetaData ) => { + const { editorScript, script, viewScript, style, editorStyle } = + blockMetaData; + + return [ editorScript, script, viewScript, style, editorStyle ] + .flat() + .filter( + ( rawFilepath ) => rawFilepath && rawFilepath.startsWith( 'file:' ) + ); +}; + +/** + * Get the block name from the meta data, removing the `woocommerce/` namespace. + * + * @param {Object} blockMetaData + * @return {string} Block name. + */ +const getBlockName = ( blockMetaData ) => { + return blockMetaData.name.split( '/' ).at( 1 ); +}; + +/** + * Get the entry point name. + * + * @param {string} entryFilePath + * @param {Object} blockMetaData + * @return {string} The entry point name. + */ +const getEntryPointName = ( entryFilePath, blockMetaData ) => { + const filePathParts = entryFilePath.split( '/' ); + filePathParts[ filePathParts.length - 2 ] = getBlockName( blockMetaData ); + return filePathParts + .join( '/' ) + .replace( srcDir, blocksBuildDir ) + .replace( '/components', '' ); +}; + +/** + * Get the entry file path. + * + * @param {string} rawFilepath Raw file path from the block.json file. + * @param {*} dir The directory the block exists in. + * @return {string} Entry file path. + */ +const getEntryFilePath = ( rawFilepath, dir ) => { + const filepath = path.join( dir, rawFilepath.replace( 'file:', '' ) ); + + return filepath + .replace( path.extname( filepath ), '' ) + .replace( /\\/g, '/' ); +}; + +/** + * Gets the absolute file path based on the entry file path, including the extension. + * + * @param {string} entryFilePath Entry file path. + * @return {string} Absolute file path. + */ +const getAbsoluteEntryFilePath = ( entryFilePath ) => { + const [ absoluteEntryFilepath ] = glob( + `${ entryFilePath }.([jt]s?(x)|?(s)css)`, + { + absolute: true, + } + ); + return absoluteEntryFilepath; +}; + +/** + * Find all directories with block.json files and get entry points for block related assets. + */ +const blockEntryPoints = getBlockMetaDataFiles().reduce( + ( accumulator, blockMetadataFile ) => { + const blockMetaData = getBlockMetaData( blockMetadataFile ); + + getBlockFileAssets( blockMetaData ).forEach( ( rawFilePath ) => { + const entryFilePath = getEntryFilePath( + rawFilePath, + path.dirname( blockMetadataFile ) + ); + + const absoluteEntryFilepath = + getAbsoluteEntryFilePath( entryFilePath ); + + if ( ! absoluteEntryFilepath ) { + // eslint-disable-next-line no-console + console.warn( 'Block asset file not found.', entryFilePath ); + return; + } + + const entryPointName = getEntryPointName( + entryFilePath, + blockMetaData + ); + + accumulator[ entryPointName ] = absoluteEntryFilepath; + } ); + return accumulator; + }, + {} +); + +module.exports = { + blocksBuildDir, + blockEntryPoints, + getBlockMetaData, + getEntryPointName, +}; diff --git a/packages/js/product-editor/package.json b/packages/js/product-editor/package.json index c0e0cc448ba..a5e2de6b022 100644 --- a/packages/js/product-editor/package.json +++ b/packages/js/product-editor/package.json @@ -89,6 +89,7 @@ "@wordpress/block-editor": "^9.8.0", "@wordpress/browserslist-config": "wp-6.0", "concurrently": "^7.0.0", + "copy-webpack-plugin": "^9.1.0", "css-loader": "^3.6.0", "eslint": "^8.32.0", "jest": "^27.5.1", diff --git a/packages/js/product-editor/src/components/details-name-block/block.json b/packages/js/product-editor/src/components/details-name-block/block.json index 15db681d2a1..d62e85caf34 100644 --- a/packages/js/product-editor/src/components/details-name-block/block.json +++ b/packages/js/product-editor/src/components/details-name-block/block.json @@ -20,5 +20,6 @@ "reusable": false, "inserter": false, "lock": false - } + }, + "editorStyle": "file:./editor.css" } diff --git a/packages/js/product-editor/src/components/pricing-block/block.json b/packages/js/product-editor/src/components/pricing-block/block.json index 90851b04cf9..59d66189f30 100644 --- a/packages/js/product-editor/src/components/pricing-block/block.json +++ b/packages/js/product-editor/src/components/pricing-block/block.json @@ -26,5 +26,6 @@ "reusable": false, "inserter": false, "lock": false - } + }, + "editorStyle": "file:./editor.css" } diff --git a/packages/js/product-editor/src/components/section/block.json b/packages/js/product-editor/src/components/section/block.json index 89f8811fb0c..cde81d65f95 100644 --- a/packages/js/product-editor/src/components/section/block.json +++ b/packages/js/product-editor/src/components/section/block.json @@ -25,5 +25,6 @@ "reusable": false, "inserter": false, "lock": false - } + }, + "editorStyle": "file:./editor.css" } diff --git a/packages/js/product-editor/src/components/section/style.scss b/packages/js/product-editor/src/components/section/editor.scss similarity index 100% rename from packages/js/product-editor/src/components/section/style.scss rename to packages/js/product-editor/src/components/section/editor.scss diff --git a/packages/js/product-editor/src/components/tab/style.scss b/packages/js/product-editor/src/components/tab/editor.scss similarity index 100% rename from packages/js/product-editor/src/components/tab/style.scss rename to packages/js/product-editor/src/components/tab/editor.scss diff --git a/packages/js/product-editor/src/style.scss b/packages/js/product-editor/src/style.scss index a4d59bce446..fdba01eac4b 100644 --- a/packages/js/product-editor/src/style.scss +++ b/packages/js/product-editor/src/style.scss @@ -6,8 +6,8 @@ @import 'components/header/style.scss'; @import 'components/images/editor.scss'; @import 'components/block-editor/style.scss'; -@import 'components/section/style.scss'; -@import 'components/tab/style.scss'; +@import 'components/section/editor.scss'; +@import 'components/tab/editor.scss'; @import 'components/tabs/style.scss'; @import 'components/details-summary-block/style.scss'; @import 'components/product-mvp-ces-footer/style.scss'; diff --git a/packages/js/product-editor/tsconfig-cjs.json b/packages/js/product-editor/tsconfig-cjs.json index 1df8b7b1f7a..92bfe004f21 100644 --- a/packages/js/product-editor/tsconfig-cjs.json +++ b/packages/js/product-editor/tsconfig-cjs.json @@ -1,7 +1,8 @@ { "extends": "../tsconfig-cjs", "include": [ - "**/*", + "**/*.d.ts", + "src/**/*", "src/**/*.json" ], "compilerOptions": { diff --git a/packages/js/product-editor/webpack.config.js b/packages/js/product-editor/webpack.config.js index 9b42746f1dd..ae2cba9f015 100644 --- a/packages/js/product-editor/webpack.config.js +++ b/packages/js/product-editor/webpack.config.js @@ -1,12 +1,29 @@ +/** + * External dependencies + */ +const CopyWebpackPlugin = require( 'copy-webpack-plugin' ); +const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' ); +const path = require( 'path' ); +const RemoveEmptyScriptsPlugin = require( 'webpack-remove-empty-scripts' ); +const WebpackRTLPlugin = require( 'webpack-rtl-plugin' ); + /** * Internal dependencies */ const { webpackConfig } = require( '@woocommerce/internal-style-build' ); +const { + blockEntryPoints, + getBlockMetaData, + getEntryPointName, +} = require( './config/block-entry-points' ); + +const NODE_ENV = process.env.NODE_ENV || 'development'; module.exports = { mode: process.env.NODE_ENV || 'development', entry: { 'build-style': __dirname + '/src/style.scss', + ...blockEntryPoints, }, output: { path: __dirname, @@ -15,5 +32,42 @@ module.exports = { parser: webpackConfig.parser, rules: webpackConfig.rules, }, - plugins: webpackConfig.plugins, + plugins: [ + new RemoveEmptyScriptsPlugin(), + new MiniCssExtractPlugin( { + filename: ( data ) => { + return data.chunk.name.startsWith( '/build/blocks' ) + ? `[name].css` + : `[name]/style.css`; + }, + chunkFilename: 'chunks/[id].style.css', + } ), + new WebpackRTLPlugin( { + test: /(?=6.9.0'} dev: true + /@babel/compat-data/7.19.3: + resolution: {integrity: sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw==} + engines: {node: '>=6.9.0'} + /@babel/compat-data/7.21.0: resolution: {integrity: sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==} engines: {node: '>=6.9.0'} @@ -2966,7 +2972,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.21.0 + '@babel/compat-data': 7.19.3 '@babel/core': 7.12.9 '@babel/helper-validator-option': 7.18.6 browserslist: 4.19.3 @@ -3116,9 +3122,9 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.21.0 - '@babel/helper-member-expression-to-functions': 7.21.0 + '@babel/helper-member-expression-to-functions': 7.18.9 '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-replace-supers': 7.19.1 '@babel/helper-split-export-declaration': 7.18.6 transitivePeerDependencies: - supports-color @@ -3266,6 +3272,12 @@ packages: dependencies: '@babel/types': 7.21.3 + /@babel/helper-member-expression-to-functions/7.18.9: + resolution: {integrity: sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.3 + /@babel/helper-member-expression-to-functions/7.21.0: resolution: {integrity: sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==} engines: {node: '>=6.9.0'} @@ -3668,36 +3680,6 @@ packages: - supports-color dev: true - /@babel/plugin-proposal-async-generator-functions/7.19.1_@babel+core@7.12.9: - resolution: {integrity: sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.12.9 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-proposal-async-generator-functions/7.19.1_@babel+core@7.17.8: - resolution: {integrity: sha512-0yu8vNATgLy4ivqMNBIwb1HebCelqN7YX8SL3FDXORv/RqT0zEEWUCH4GH44JsSrvCu6GqnAdR5EBFAPeNBB4Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.17.8 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.17.8 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/plugin-proposal-async-generator-functions/7.20.7_@babel+core@7.12.9: resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==} engines: {node: '>=6.9.0'} @@ -4308,34 +4290,6 @@ packages: '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.21.3 dev: true - /@babel/plugin-proposal-object-rest-spread/7.18.9_@babel+core@7.12.9: - resolution: {integrity: sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/compat-data': 7.21.0 - '@babel/core': 7.12.9 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.12.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.12.9 - '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.12.9 - dev: true - - /@babel/plugin-proposal-object-rest-spread/7.18.9_@babel+core@7.17.8: - resolution: {integrity: sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/compat-data': 7.21.0 - '@babel/core': 7.17.8 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.17.8 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.17.8 - '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.17.8 - dev: true - /@babel/plugin-proposal-object-rest-spread/7.20.7_@babel+core@7.12.9: resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==} engines: {node: '>=6.9.0'} @@ -5144,7 +5098,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.19.0 /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.17.8: resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} @@ -5519,26 +5473,6 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-block-scoping/7.18.9_@babel+core@7.12.9: - resolution: {integrity: sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-block-scoping/7.18.9_@babel+core@7.17.8: - resolution: {integrity: sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.17.8 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - /@babel/plugin-transform-block-scoping/7.21.0_@babel+core@7.12.9: resolution: {integrity: sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==} engines: {node: '>=6.9.0'} @@ -5596,7 +5530,7 @@ packages: '@babel/helper-function-name': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-replace-supers': 7.19.1 '@babel/helper-split-export-declaration': 7.18.6 globals: 11.12.0 transitivePeerDependencies: @@ -5615,47 +5549,7 @@ packages: '@babel/helper-function-name': 7.21.0 '@babel/helper-optimise-call-expression': 7.18.6 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-split-export-declaration': 7.18.6 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-classes/7.19.0_@babel+core@7.12.9: - resolution: {integrity: sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.12.9 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.20.7 - '@babel/helper-split-export-declaration': 7.18.6 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-classes/7.19.0_@babel+core@7.17.8: - resolution: {integrity: sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.17.8 - '@babel/helper-annotate-as-pure': 7.18.6 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.17.8 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.21.0 - '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-replace-supers': 7.19.1 '@babel/helper-split-export-declaration': 7.18.6 globals: 11.12.0 transitivePeerDependencies: @@ -5796,26 +5690,6 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-destructuring/7.18.13_@babel+core@7.12.9: - resolution: {integrity: sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-destructuring/7.18.13_@babel+core@7.17.8: - resolution: {integrity: sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.17.8 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - /@babel/plugin-transform-destructuring/7.21.3_@babel+core@7.12.9: resolution: {integrity: sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==} engines: {node: '>=6.9.0'} @@ -6260,34 +6134,6 @@ packages: - supports-color dev: true - /@babel/plugin-transform-modules-amd/7.18.6_@babel+core@7.12.9: - resolution: {integrity: sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - babel-plugin-dynamic-import-node: 2.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-modules-amd/7.18.6_@babel+core@7.17.8: - resolution: {integrity: sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.17.8 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - babel-plugin-dynamic-import-node: 2.3.3 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/plugin-transform-modules-amd/7.20.11_@babel+core@7.12.9: resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==} engines: {node: '>=6.9.0'} @@ -6349,7 +6195,7 @@ packages: '@babel/core': 7.17.8 '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-simple-access': 7.20.2 + '@babel/helper-simple-access': 7.18.6 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: - supports-color @@ -6363,37 +6209,7 @@ packages: '@babel/core': 7.21.3 '@babel/helper-module-transforms': 7.21.2 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-simple-access': 7.20.2 - babel-plugin-dynamic-import-node: 2.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-modules-commonjs/7.18.6_@babel+core@7.12.9: - resolution: {integrity: sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-simple-access': 7.20.2 - babel-plugin-dynamic-import-node: 2.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-modules-commonjs/7.18.6_@babel+core@7.17.8: - resolution: {integrity: sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.17.8 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-simple-access': 7.20.2 + '@babel/helper-simple-access': 7.18.6 babel-plugin-dynamic-import-node: 2.3.3 transitivePeerDependencies: - supports-color @@ -6470,38 +6286,6 @@ packages: - supports-color dev: true - /@babel/plugin-transform-modules-systemjs/7.19.0_@babel+core@7.12.9: - resolution: {integrity: sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-identifier': 7.19.1 - babel-plugin-dynamic-import-node: 2.3.3 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/plugin-transform-modules-systemjs/7.19.0_@babel+core@7.17.8: - resolution: {integrity: sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.17.8 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-module-transforms': 7.21.2 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-identifier': 7.19.1 - babel-plugin-dynamic-import-node: 2.3.3 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/plugin-transform-modules-systemjs/7.20.11_@babel+core@7.12.9: resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==} engines: {node: '>=6.9.0'} @@ -6727,7 +6511,7 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-replace-supers': 7.19.1 transitivePeerDependencies: - supports-color dev: true @@ -6798,26 +6582,6 @@ packages: '@babel/helper-plugin-utils': 7.20.2 dev: true - /@babel/plugin-transform-parameters/7.18.8_@babel+core@7.12.9: - resolution: {integrity: sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.12.9 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - - /@babel/plugin-transform-parameters/7.18.8_@babel+core@7.17.8: - resolution: {integrity: sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.17.8 - '@babel/helper-plugin-utils': 7.20.2 - dev: true - /@babel/plugin-transform-parameters/7.21.3_@babel+core@7.12.9: resolution: {integrity: sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==} engines: {node: '>=6.9.0'} @@ -7772,11 +7536,11 @@ packages: '@babel/compat-data': 7.21.0 '@babel/core': 7.12.9 '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.12.9 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-validator-option': 7.18.6 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.12.9 '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-proposal-async-generator-functions': 7.19.1_@babel+core@7.12.9 + '@babel/plugin-proposal-async-generator-functions': 7.20.7_@babel+core@7.12.9 '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.12.9 '@babel/plugin-proposal-class-static-block': 7.18.6_@babel+core@7.12.9 '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.12.9 @@ -7785,7 +7549,7 @@ packages: '@babel/plugin-proposal-logical-assignment-operators': 7.18.9_@babel+core@7.12.9 '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.12.9 '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-proposal-object-rest-spread': 7.18.9_@babel+core@7.12.9 + '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.12.9 '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.12.9 '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.12.9 '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.12.9 @@ -7808,10 +7572,10 @@ packages: '@babel/plugin-transform-arrow-functions': 7.18.6_@babel+core@7.12.9 '@babel/plugin-transform-async-to-generator': 7.18.6_@babel+core@7.12.9 '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-block-scoping': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-transform-classes': 7.19.0_@babel+core@7.12.9 + '@babel/plugin-transform-block-scoping': 7.21.0_@babel+core@7.12.9 + '@babel/plugin-transform-classes': 7.21.0_@babel+core@7.12.9 '@babel/plugin-transform-computed-properties': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-transform-destructuring': 7.18.13_@babel+core@7.12.9 + '@babel/plugin-transform-destructuring': 7.21.3_@babel+core@7.12.9 '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.12.9 '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.12.9 '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.12.9 @@ -7819,14 +7583,14 @@ packages: '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.12.9 '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.12.9 '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-modules-amd': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-modules-commonjs': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-modules-systemjs': 7.19.0_@babel+core@7.12.9 + '@babel/plugin-transform-modules-amd': 7.20.11_@babel+core@7.12.9 + '@babel/plugin-transform-modules-commonjs': 7.21.2_@babel+core@7.12.9 + '@babel/plugin-transform-modules-systemjs': 7.20.11_@babel+core@7.12.9 '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.12.9 '@babel/plugin-transform-named-capturing-groups-regex': 7.19.1_@babel+core@7.12.9 '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.12.9 '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-parameters': 7.18.8_@babel+core@7.12.9 + '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.12.9 '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.12.9 '@babel/plugin-transform-regenerator': 7.18.6_@babel+core@7.12.9 '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.12.9 @@ -7857,11 +7621,11 @@ packages: '@babel/compat-data': 7.21.0 '@babel/core': 7.17.8 '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.17.8 - '@babel/helper-plugin-utils': 7.19.0 + '@babel/helper-plugin-utils': 7.20.2 '@babel/helper-validator-option': 7.18.6 '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.17.8 '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-proposal-async-generator-functions': 7.19.1_@babel+core@7.17.8 + '@babel/plugin-proposal-async-generator-functions': 7.20.7_@babel+core@7.17.8 '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.17.8 '@babel/plugin-proposal-class-static-block': 7.18.6_@babel+core@7.17.8 '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.17.8 @@ -7870,7 +7634,7 @@ packages: '@babel/plugin-proposal-logical-assignment-operators': 7.18.9_@babel+core@7.17.8 '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.17.8 '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-proposal-object-rest-spread': 7.18.9_@babel+core@7.17.8 + '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.17.8 '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.17.8 '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.17.8 '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.17.8 @@ -7893,10 +7657,10 @@ packages: '@babel/plugin-transform-arrow-functions': 7.18.6_@babel+core@7.17.8 '@babel/plugin-transform-async-to-generator': 7.18.6_@babel+core@7.17.8 '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-block-scoping': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-transform-classes': 7.19.0_@babel+core@7.17.8 + '@babel/plugin-transform-block-scoping': 7.21.0_@babel+core@7.17.8 + '@babel/plugin-transform-classes': 7.21.0_@babel+core@7.17.8 '@babel/plugin-transform-computed-properties': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-transform-destructuring': 7.18.13_@babel+core@7.17.8 + '@babel/plugin-transform-destructuring': 7.21.3_@babel+core@7.17.8 '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.17.8 '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.17.8 '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.17.8 @@ -7904,14 +7668,14 @@ packages: '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.17.8 '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.17.8 '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-modules-amd': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-modules-commonjs': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-modules-systemjs': 7.19.0_@babel+core@7.17.8 + '@babel/plugin-transform-modules-amd': 7.20.11_@babel+core@7.17.8 + '@babel/plugin-transform-modules-commonjs': 7.21.2_@babel+core@7.17.8 + '@babel/plugin-transform-modules-systemjs': 7.20.11_@babel+core@7.17.8 '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.17.8 '@babel/plugin-transform-named-capturing-groups-regex': 7.19.1_@babel+core@7.17.8 '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.17.8 '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-parameters': 7.18.8_@babel+core@7.17.8 + '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.17.8 '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.17.8 '@babel/plugin-transform-regenerator': 7.18.6_@babel+core@7.17.8 '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.17.8 @@ -9233,7 +8997,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 16.18.18 + '@types/node': 16.18.21 chalk: 4.1.2 jest-message-util: 26.6.2 jest-util: 26.6.2 @@ -9245,7 +9009,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 chalk: 4.1.2 jest-message-util: 27.5.1 jest-util: 27.5.1 @@ -9337,7 +9101,7 @@ packages: '@jest/test-result': 26.6.2 '@jest/transform': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 16.18.18 + '@types/node': 16.18.21 ansi-escapes: 4.3.2 chalk: 4.1.2 exit: 0.1.2 @@ -9382,7 +9146,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.8.1 @@ -9445,7 +9209,7 @@ packages: dependencies: '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 16.18.18 + '@types/node': 16.18.21 jest-mock: 26.6.2 dev: true @@ -9455,7 +9219,7 @@ packages: dependencies: '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 jest-mock: 27.5.1 /@jest/fake-timers/24.9.0: @@ -9486,7 +9250,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@sinonjs/fake-timers': 6.0.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 jest-message-util: 26.6.2 jest-mock: 26.6.2 jest-util: 26.6.2 @@ -9498,7 +9262,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@sinonjs/fake-timers': 8.1.0 - '@types/node': 16.18.18 + '@types/node': 16.18.21 jest-message-util: 27.5.1 jest-mock: 27.5.1 jest-util: 27.5.1 @@ -9642,7 +9406,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 @@ -9913,7 +9677,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 3.0.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 '@types/yargs': 15.0.14 chalk: 4.1.2 @@ -9923,7 +9687,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.3 '@types/istanbul-reports': 3.0.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 '@types/yargs': 16.0.4 chalk: 4.1.2 @@ -10610,7 +10374,7 @@ packages: engines: {node: '>=14'} hasBin: true dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 playwright-core: 1.30.0 dev: true @@ -11030,14 +10794,14 @@ packages: resolution: {integrity: sha512-OkIJpiU2fz6HOJujhlhfIGrc8hB4ibqtf7nnbJQDerG0BqwZCfmgtK5sWzZ0TkXVRBKD5MpLrTmCYyMxoMCgPw==} engines: {node: '>= 8.9.0', npm: '>= 5.5.1'} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: false /@slack/logger/3.0.0: resolution: {integrity: sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA==} engines: {node: '>= 12.13.0', npm: '>= 6.12.0'} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: false /@slack/types/1.10.0: @@ -11057,7 +10821,7 @@ packages: '@slack/logger': 2.0.0 '@slack/types': 1.10.0 '@types/is-stream': 1.1.0 - '@types/node': 16.18.18 + '@types/node': 16.18.21 axios: 0.21.4 eventemitter3: 3.1.2 form-data: 2.5.1 @@ -11075,7 +10839,7 @@ packages: '@slack/logger': 3.0.0 '@slack/types': 2.4.0 '@types/is-stream': 1.1.0 - '@types/node': 16.18.18 + '@types/node': 16.18.21 axios: 0.24.0 eventemitter3: 3.1.2 form-data: 2.5.1 @@ -12629,7 +12393,7 @@ packages: interpret: 2.2.0 json5: 2.2.3 lazy-universal-dotenv: 3.0.1 - picomatch: 2.3.1 + picomatch: 2.3.0 pkg-dir: 5.0.0 pretty-hrtime: 1.0.3 react: 17.0.2 @@ -14359,13 +14123,13 @@ packages: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/bonjour/3.5.10: resolution: {integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/cacheable-request/6.0.2: @@ -14373,18 +14137,18 @@ packages: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 16.18.18 + '@types/node': 16.18.21 '@types/responselike': 1.0.0 /@types/cheerio/0.22.30: resolution: {integrity: sha512-t7ZVArWZlq3dFa9Yt33qFBQIK4CQd1Q3UJp0V+UhP6vgLWLM6Qug7vZuRSGXg45zXeB1Fm5X2vmBkEX58LV2Tw==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 /@types/cli-progress/3.11.0: resolution: {integrity: sha512-XhXhBv1R/q2ahF3BM7qT5HLzJNlIL0wbcGyZVjqOTqAybAnsLisd7gy1UCyIqpL+5Iv6XhlSyzjLCnI2sIdbCg==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: false /@types/color-convert/2.0.0: @@ -14405,13 +14169,13 @@ packages: resolution: {integrity: sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==} dependencies: '@types/express-serve-static-core': 4.17.31 - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/connect/3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/cookie/0.4.1: @@ -14421,7 +14185,7 @@ packages: /@types/create-hmac/1.1.0: resolution: {integrity: sha512-BNYNdzdhOZZQWCOpwvIll3FSvgo3e55Y2M6s/jOY6TuOCwqt3cLmQsK4tSmJ5fayDot8EG4k3+hcZagfww9JlQ==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/d3-time-format/2.3.1: @@ -14473,7 +14237,7 @@ packages: /@types/express-serve-static-core/4.17.31: resolution: {integrity: sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 '@types/qs': 6.9.7 '@types/range-parser': 1.2.4 dev: true @@ -14491,13 +14255,13 @@ packages: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 3.0.5 - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/graceful-fs/4.1.5: resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 /@types/hast/2.3.4: resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==} @@ -14523,7 +14287,7 @@ packages: /@types/http-proxy/1.17.10: resolution: {integrity: sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/is-function/1.0.1: @@ -14533,7 +14297,7 @@ packages: /@types/is-stream/1.1.0: resolution: {integrity: sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: false /@types/istanbul-lib-coverage/2.0.3: @@ -14580,7 +14344,7 @@ packages: /@types/keyv/3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 /@types/lodash.shuffle/4.2.7: resolution: {integrity: sha512-b+K0NBpB4WcNoQTfifuTmi5nm5mJXRw9DBdbFfBr1q1+EVoTKkClDxq/7r1sq2GZcRelMFRsFcGGHrHQgxRySg==} @@ -14634,14 +14398,14 @@ packages: /@types/node-fetch/2.6.1: resolution: {integrity: sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 form-data: 3.0.1 dev: true /@types/node-fetch/2.6.2: resolution: {integrity: sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 form-data: 3.0.1 dev: true @@ -14657,8 +14421,8 @@ packages: resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} dev: true - /@types/node/16.18.18: - resolution: {integrity: sha512-fwGw1uvQAzabxL1pyoknPlJIF2t7+K90uTqynleKRx24n3lYcxWa3+KByLhgkF8GEAK2c7hC8Ki0RkNM5H15jQ==} + /@types/node/16.18.21: + resolution: {integrity: sha512-TassPGd0AEZWA10qcNnXnSNwHlLfSth8XwUaWc3gTSDmBz/rKb613Qw5qRf6o2fdRBrLbsgeC9PMZshobkuUqg==} /@types/normalize-package-data/2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -14703,13 +14467,13 @@ packages: /@types/puppeteer/4.0.2: resolution: {integrity: sha512-LOjNvVmJR9X2K7/hUJlt1VHss4VjNOLml27i21PJfwdQLGxxXq47mPRqcY54LR1J2IoFdyM0WFYddWFhFM51pw==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/puppeteer/5.4.5: resolution: {integrity: sha512-lxCjpDEY+DZ66+W3x5Af4oHnEmUXt0HuaRzkBGE2UZiZEp/V1d3StpLPlmNVu/ea091bdNmVPl44lu8Wy/0ZCA==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/q/1.5.5: @@ -14781,7 +14545,7 @@ packages: /@types/responselike/1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 /@types/retry/0.12.1: resolution: {integrity: sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==} @@ -14802,7 +14566,7 @@ packages: resolution: {integrity: sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==} dependencies: '@types/mime': 3.0.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/sizzle/2.3.3: @@ -14812,7 +14576,7 @@ packages: /@types/sockjs/0.3.33: resolution: {integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/source-list-map/0.1.2: @@ -14872,7 +14636,7 @@ packages: resolution: {integrity: sha512-ayJ0iOCDNHnKpKTgBG6Q6JOnHTj9zFta+3j2b8Ejza0e4cvRyMn0ZoLEmbPrTHe5YYRlDYPvPWVdV4cTaRyH7g==} dependencies: '@types/expect': 1.20.4 - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/webpack-env/1.16.3: @@ -14882,7 +14646,7 @@ packages: /@types/webpack-sources/0.1.9: resolution: {integrity: sha512-bvzMnzqoK16PQIC8AYHNdW45eREJQMd6WG/msQWX5V2+vZmODCOPb4TJcbgRljTZZTwTM4wUMcsI8FftNA7new==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 '@types/source-list-map': 0.1.2 source-map: 0.6.1 dev: true @@ -14890,7 +14654,7 @@ packages: /@types/webpack/4.41.32: resolution: {integrity: sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 '@types/tapable': 1.0.8 '@types/uglify-js': 3.13.1 '@types/webpack-sources': 0.1.9 @@ -15054,7 +14818,7 @@ packages: /@types/ws/8.5.4: resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /@types/yargs-parser/20.2.1: @@ -15080,7 +14844,7 @@ packages: resolution: {integrity: sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==} requiresBuild: true dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 optional: true /@typescript-eslint/eslint-plugin/4.33.0_s2qqtxhzmb7vugvfoyripfgp7i: @@ -18564,8 +18328,8 @@ packages: puppeteer: /puppeteer-core/3.0.0 read-pkg-up: 1.0.1 resolve-bin: 0.4.3 - sass: 1.59.3 - sass-loader: 8.0.2_sass@1.59.3+webpack@4.46.0 + sass: 1.60.0 + sass-loader: 8.0.2_sass@1.60.0+webpack@4.46.0 source-map-loader: 0.2.4 stylelint: 13.13.1 stylelint-config-wordpress: 17.0.0_stylelint@13.13.1 @@ -22493,6 +22257,21 @@ packages: webpack: 5.76.3 dev: true + /copy-webpack-plugin/9.1.0_webpack@5.70.0: + resolution: {integrity: sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^5.1.0 + dependencies: + fast-glob: 3.2.11 + glob-parent: 6.0.2 + globby: 11.1.0 + normalize-path: 3.0.0 + schema-utils: 3.1.1 + serialize-javascript: 6.0.0 + webpack: 5.70.0_webpack-cli@3.3.12 + dev: true + /core-js-compat/3.19.1: resolution: {integrity: sha512-Q/VJ7jAF/y68+aUsQJ/afPOewdsGkDtcMb40J8MbuWKlK3Y+wtHq8bTHKPj2WKWLIqmS5JhHs4CzHtz6pT2W6g==} dependencies: @@ -25736,7 +25515,7 @@ packages: '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.5 + micromatch: 4.0.4 dev: true /fast-json-parse/1.0.3: @@ -29305,7 +29084,7 @@ packages: '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 '@types/babel__traverse': 7.14.2 - '@types/node': 16.18.18 + '@types/node': 16.18.21 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -29336,7 +29115,7 @@ packages: '@jest/environment': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 chalk: 4.1.2 co: 4.6.0 dedent: 0.7.0 @@ -29767,7 +29546,7 @@ packages: '@jest/environment': 26.6.2 '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 16.18.18 + '@types/node': 16.18.21 jest-mock: 26.6.2 jest-util: 26.6.2 jsdom: 16.7.0 @@ -29785,7 +29564,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 jest-mock: 27.5.1 jest-util: 27.5.1 jsdom: 16.7.0 @@ -29827,7 +29606,7 @@ packages: '@jest/environment': 26.6.2 '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 16.18.18 + '@types/node': 16.18.21 jest-mock: 26.6.2 jest-util: 26.6.2 dev: true @@ -29839,7 +29618,7 @@ packages: '@jest/environment': 27.5.1 '@jest/fake-timers': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 jest-mock: 27.5.1 jest-util: 27.5.1 @@ -29934,7 +29713,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@types/graceful-fs': 4.1.5 - '@types/node': 16.18.18 + '@types/node': 16.18.21 anymatch: 3.1.2 fb-watchman: 2.0.1 graceful-fs: 4.2.9 @@ -29956,7 +29735,7 @@ packages: dependencies: '@jest/types': 27.5.1 '@types/graceful-fs': 4.1.5 - '@types/node': 16.18.18 + '@types/node': 16.18.21 anymatch: 3.1.2 fb-watchman: 2.0.1 graceful-fs: 4.2.9 @@ -30030,7 +29809,7 @@ packages: '@jest/source-map': 26.6.2 '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 16.18.18 + '@types/node': 16.18.21 chalk: 4.1.2 co: 4.6.0 expect: 26.6.2 @@ -30059,7 +29838,7 @@ packages: '@jest/source-map': 27.5.1 '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 chalk: 4.1.2 co: 4.6.0 expect: 27.5.1 @@ -30233,7 +30012,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 16.18.18 + '@types/node': 16.18.21 dev: true /jest-mock/27.5.1: @@ -30241,7 +30020,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 /jest-pnp-resolver/1.2.2_jest-resolve@24.9.0: resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} @@ -30507,7 +30286,7 @@ packages: '@jest/environment': 26.6.2 '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 16.18.18 + '@types/node': 16.18.21 chalk: 4.1.2 emittery: 0.7.2 exit: 0.1.2 @@ -30540,7 +30319,7 @@ packages: '@jest/test-result': 27.5.1 '@jest/transform': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 chalk: 4.1.2 emittery: 0.8.1 graceful-fs: 4.2.9 @@ -30719,14 +30498,14 @@ packages: resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} engines: {node: '>= 10.14.2'} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 graceful-fs: 4.2.9 /jest-serializer/27.5.1: resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 graceful-fs: 4.2.9 /jest-snapshot/24.9.0: @@ -30860,7 +30639,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 16.18.18 + '@types/node': 16.18.21 chalk: 4.1.2 graceful-fs: 4.2.9 is-ci: 2.0.0 @@ -30871,7 +30650,7 @@ packages: engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} dependencies: '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 chalk: 4.1.2 ci-info: 3.2.0 graceful-fs: 4.2.9 @@ -30956,7 +30735,7 @@ packages: dependencies: '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 16.18.18 + '@types/node': 16.18.21 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 26.6.2 @@ -30969,7 +30748,7 @@ packages: dependencies: '@jest/test-result': 27.5.1 '@jest/types': 27.5.1 - '@types/node': 16.18.18 + '@types/node': 16.18.21 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 27.5.1 @@ -30995,7 +30774,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 merge-stream: 2.0.0 supports-color: 7.2.0 @@ -31003,7 +30782,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 16.18.18 + '@types/node': 16.18.21 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -33419,8 +33198,8 @@ packages: hasBin: true dev: true - /nanoid/3.3.4: - resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} + /nanoid/3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -33686,8 +33465,8 @@ packages: is: 3.3.0 dev: false - /nodemon/2.0.21: - resolution: {integrity: sha512-djN/n2549DUtY33S7o1djRCd7dEm0kBnj9c7S9XVXqRUbuggN1MZH/Nqa+5RFQr63Fbefq37nFXAE9VU86yL1A==} + /nodemon/2.0.22: + resolution: {integrity: sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==} engines: {node: '>=8.10.0'} hasBin: true dependencies: @@ -35808,7 +35587,7 @@ packages: resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} engines: {node: ^10 || ^12 || >=14} dependencies: - nanoid: 3.3.4 + nanoid: 3.3.6 picocolors: 1.0.0 source-map-js: 1.0.2 @@ -38247,7 +38026,7 @@ packages: semver: 7.3.5 webpack: 5.70.0_webpack-cli@3.3.12 - /sass-loader/10.4.1_sass@1.59.3+webpack@5.76.3: + /sass-loader/10.4.1_sass@1.60.0+webpack@5.76.3: resolution: {integrity: sha512-aX/iJZTTpNUNx/OSYzo2KsjIUQHqvWsAhhUijFjAPdZTEhstjZI9zTNvkTTwsx+uNUJqUwOw5gacxQMx4hJxGQ==} engines: {node: '>= 10.13.0'} peerDependencies: @@ -38266,7 +38045,7 @@ packages: klona: 2.0.5 loader-utils: 2.0.4 neo-async: 2.6.2 - sass: 1.59.3 + sass: 1.60.0 schema-utils: 3.1.1 semver: 7.3.8 webpack: 5.76.3 @@ -38297,7 +38076,7 @@ packages: webpack: 5.70.0_bgqcrdgdviybk52kjcpjat65sa dev: true - /sass-loader/8.0.2_sass@1.59.3+webpack@4.46.0: + /sass-loader/8.0.2_sass@1.60.0+webpack@4.46.0: resolution: {integrity: sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==} engines: {node: '>= 8.9.0'} peerDependencies: @@ -38316,7 +38095,7 @@ packages: clone-deep: 4.0.1 loader-utils: 1.4.0 neo-async: 2.6.2 - sass: 1.59.3 + sass: 1.60.0 schema-utils: 2.7.1 semver: 6.3.0 webpack: 4.46.0_webpack-cli@3.3.12 @@ -38332,8 +38111,8 @@ packages: source-map-js: 1.0.2 dev: true - /sass/1.59.3: - resolution: {integrity: sha512-QCq98N3hX1jfTCoUAsF3eyGuXLsY7BCnCEg9qAact94Yc21npG2/mVOqoDvE0fCbWDqiM4WlcJQla0gWG2YlxQ==} + /sass/1.60.0: + resolution: {integrity: sha512-updbwW6fNb5gGm8qMXzVO7V4sWf7LMXnMly/JEyfbfERbVH46Fn6q02BX7/eHTdKpE7d+oTkMMQpFWNUMfFbgQ==} engines: {node: '>=12.0.0'} hasBin: true dependencies: @@ -40863,7 +40642,7 @@ packages: webpack: 5.76.3_bgqcrdgdviybk52kjcpjat65sa dev: true - /ts-node/10.9.1_z2kt25o33of7k4uzjztqjquebu: + /ts-node/10.9.1_kmfuqqg37ytwipeijbkfhsguau: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -40882,7 +40661,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 16.18.18 + '@types/node': 16.18.21 acorn: 8.7.0 acorn-walk: 8.2.0 arg: 4.1.3 @@ -41484,7 +41263,7 @@ packages: webpack: ^3.0.0 || ^4.0.0 dependencies: loader-utils: 1.4.0 - mime: 2.5.2 + mime: 2.6.0 schema-utils: 1.0.0 webpack: 5.70.0_webpack-cli@4.9.2 dev: true @@ -41496,7 +41275,7 @@ packages: webpack: ^3.0.0 || ^4.0.0 dependencies: loader-utils: 1.4.0 - mime: 2.5.2 + mime: 2.6.0 schema-utils: 1.0.0 webpack: 5.76.3 dev: true From 6df1cef307fcff327276f35f20b58a466bffb5f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maikel=20David=20P=C3=A9rez=20G=C3=B3mez?= Date: Fri, 31 Mar 2023 14:47:31 -0300 Subject: [PATCH 106/466] Add Sale price and list price blocks to pricing tab with pricing section (#37513) * Add pricing section to the pricing tab * Add changelog file * Fix php linter errors --- .../js/product-editor/changelog/add-37390 | 4 ++ plugins/woocommerce/changelog/add-37390 | 4 ++ .../includes/class-wc-post-types.php | 56 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 packages/js/product-editor/changelog/add-37390 create mode 100644 plugins/woocommerce/changelog/add-37390 diff --git a/packages/js/product-editor/changelog/add-37390 b/packages/js/product-editor/changelog/add-37390 new file mode 100644 index 00000000000..ec8d62081e0 --- /dev/null +++ b/packages/js/product-editor/changelog/add-37390 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add pricing section to the pricing tab diff --git a/plugins/woocommerce/changelog/add-37390 b/plugins/woocommerce/changelog/add-37390 new file mode 100644 index 00000000000..ec8d62081e0 --- /dev/null +++ b/plugins/woocommerce/changelog/add-37390 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add pricing section to the pricing tab diff --git a/plugins/woocommerce/includes/class-wc-post-types.php b/plugins/woocommerce/includes/class-wc-post-types.php index 2cf291c43a4..77114474dd5 100644 --- a/plugins/woocommerce/includes/class-wc-post-types.php +++ b/plugins/woocommerce/includes/class-wc-post-types.php @@ -460,6 +460,62 @@ class WC_Post_Types { 'id' => 'pricing', 'title' => __( 'Pricing', 'woocommerce' ), ), + array( + array( + 'woocommerce/product-section', + array( + 'title' => __( 'Pricing', 'woocommerce' ), + 'description' => sprintf( + /* translators: %1$s: Images guide link opening tag. %2$s: Images guide link closing tag.*/ + __( 'Set a competitive price, put the product on sale, and manage tax calculations. %1$sHow to price your product?%2$s', 'woocommerce' ), + '', + '' + ), + 'icon' => array( + 'src' => '', + ), + ), + array( + array( + 'core/columns', + array(), + array( + array( + 'core/column', + array( + 'templateLock' => 'all', + ), + array( + array( + 'woocommerce/product-pricing', + array( + 'name' => 'regular_price', + 'label' => __( 'List price', 'woocommerce' ), + 'showPricingSection' => true, + ), + ), + ), + ), + array( + 'core/column', + array( + 'templateLock' => 'all', + ), + array( + array( + 'woocommerce/product-pricing', + array( + 'name' => 'sale_price', + 'label' => __( 'Sale price', 'woocommerce' ), + ), + ), + ), + ), + ), + ), + ), + ), + ), ), array( 'woocommerce/product-tab', From d625f72e2bab1033cd3f5e3cafc99a747de13dc7 Mon Sep 17 00:00:00 2001 From: Fernando Marichal Date: Fri, 31 Mar 2023 16:40:16 -0300 Subject: [PATCH 107/466] New empty state for variations (#37411) * Add variations empty state # Conflicts: # plugins/woocommerce/client/legacy/css/admin.scss # plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php * Fix styles * Add changelog * Fix html * Fix js * Show and hide select and button * Fix e2e tests # Conflicts: # plugins/woocommerce/tests/e2e-pw/tests/merchant/create-variable-product.spec.js * Fix lint * Fix hidden style * Fix js for hidden elements * Fix lint * Transform `load_variations` into a promise * Fix e2e test * Fix tests * Remove comment * Fix styles * Fix comment * Remove async * Remove async --------- Co-authored-by: Fernando Marichal --- .../product_data/no-variation-arrow.svg | 3 + .../no-variation-background-image.svg | 8 + ...dev-37147_empty_state_no_variation_created | 4 + .../woocommerce/client/legacy/css/admin.scss | 661 +++++++++--------- .../js/admin/meta-boxes-product-variation.js | 140 ++-- .../views/html-product-data-variations.php | 29 +- .../merchant/create-variable-product.spec.js | 14 +- 7 files changed, 489 insertions(+), 370 deletions(-) create mode 100644 plugins/woocommerce/assets/images/product_data/no-variation-arrow.svg create mode 100644 plugins/woocommerce/assets/images/product_data/no-variation-background-image.svg create mode 100644 plugins/woocommerce/changelog/dev-37147_empty_state_no_variation_created diff --git a/plugins/woocommerce/assets/images/product_data/no-variation-arrow.svg b/plugins/woocommerce/assets/images/product_data/no-variation-arrow.svg new file mode 100644 index 00000000000..551d585d6d4 --- /dev/null +++ b/plugins/woocommerce/assets/images/product_data/no-variation-arrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/plugins/woocommerce/assets/images/product_data/no-variation-background-image.svg b/plugins/woocommerce/assets/images/product_data/no-variation-background-image.svg new file mode 100644 index 00000000000..bbb5d8122d7 --- /dev/null +++ b/plugins/woocommerce/assets/images/product_data/no-variation-background-image.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/woocommerce/changelog/dev-37147_empty_state_no_variation_created b/plugins/woocommerce/changelog/dev-37147_empty_state_no_variation_created new file mode 100644 index 00000000000..48fb332d92f --- /dev/null +++ b/plugins/woocommerce/changelog/dev-37147_empty_state_no_variation_created @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +New empty state for variations - no variations have been created yet diff --git a/plugins/woocommerce/client/legacy/css/admin.scss b/plugins/woocommerce/client/legacy/css/admin.scss index 91838040e51..db87b9c7344 100644 --- a/plugins/woocommerce/client/legacy/css/admin.scss +++ b/plugins/woocommerce/client/legacy/css/admin.scss @@ -6,10 +6,10 @@ /** * Imports */ -@import 'mixins'; -@import 'variables'; -@import 'animation'; -@import 'fonts'; +@import "mixins"; +@import "variables"; +@import "animation"; +@import "fonts"; /** * Styling begins @@ -20,7 +20,7 @@ .wc-addons-wrap { .marketplace-header { - background-image: url( ../images/marketplace-header-bg@2x.png ); + background-image: url(../images/marketplace-header-bg@2x.png); background-position: right; background-size: cover; box-sizing: border-box; @@ -85,7 +85,7 @@ height: 60px; margin: 0 0 16px; - @media only screen and ( min-width: 768px ) { + @media only screen and (min-width: 768px) { margin-bottom: 24px; } } @@ -97,7 +97,7 @@ position: relative; width: 100%; - @media only screen and ( min-width: 600px ) { + @media only screen and (min-width: 600px) { width: 288px; } @@ -115,13 +115,13 @@ width: 100%; z-index: 10; - @media only screen and ( min-width: 600px ) { + @media only screen and (min-width: 600px) { border: 1px solid #1e1e1e; left: -1px; top: 48px; } - @media only screen and ( min-width: 1100px ) { + @media only screen and (min-width: 1100px) { justify-content: center; } @@ -131,8 +131,8 @@ margin: 0; &.current a::after { - background-image: url( ../images/icons/gridicons-checkmark.svg ); - content: ''; + background-image: url(../images/icons/gridicons-checkmark.svg); + content: ""; display: block; height: 20px; position: absolute; @@ -157,7 +157,7 @@ position: relative; width: 100%; - @media only screen and ( min-width: 600px ) { + @media only screen and (min-width: 600px) { padding: 10px 18px; } } @@ -177,9 +177,9 @@ } .current-section-name::after { - background-image: url( ../images/icons/gridicons-chevron-down.svg ); + background-image: url(../images/icons/gridicons-chevron-down.svg); background-size: contain; - content: ''; + content: ""; display: block; height: 20px; position: absolute; @@ -194,7 +194,7 @@ } .current-section-name::after { - transform: rotate( 0.5turn ); + transform: rotate(0.5turn); } } @@ -214,8 +214,8 @@ /** * Marketplace related variables */ - $font-sf-pro-text: helveticaneue-light, 'Helvetica Neue Light', - 'Helvetica Neue', sans-serif; + $font-sf-pro-text: helveticaneue-light, "Helvetica Neue Light", + "Helvetica Neue", sans-serif; $font-sf-pro-display: sans-serif; @@ -237,11 +237,11 @@ } .subsubsub li::after { - content: '|'; + content: "|"; } .subsubsub li:last-child::after { - content: ''; + content: ""; } .addons-button { @@ -316,7 +316,7 @@ flex-direction: row; justify-content: center; - @media screen and ( min-width: 500px ) { + @media screen and (min-width: 500px) { justify-content: left; } @@ -340,7 +340,7 @@ padding: 0 0.5em; } - .addons-column:nth-child( 2 ) { + .addons-column:nth-child(2) { margin-right: 0; } @@ -429,7 +429,7 @@ opacity: 0.8; } - @media only screen and ( max-width: 400px ) { + @media only screen and (max-width: 400px) { .addons-button { width: 100%; } @@ -497,13 +497,13 @@ .product.addons-product-banner, .product.addons-buttons-banner { - max-width: calc( 100% - 2px ); + max-width: calc(100% - 2px); } - @media screen and ( min-width: 960px ) { + @media screen and (min-width: 960px) { // Adjust heading titles font for three-column product groups &.addons-products-three-column li.product { - max-width: calc( 33.33% - 12px ); + max-width: calc(33.33% - 12px); h2, h3 { @@ -522,7 +522,7 @@ flex-direction: column; justify-content: space-between; margin: 12px 0; - max-width: calc( 50% - 12px ); + max-width: calc(50% - 12px); min-width: 280px; min-height: 220px; overflow: hidden; @@ -533,7 +533,7 @@ max-width: 100%; } - @media only screen and ( max-width: 768px ) { + @media only screen and (max-width: 768px) { max-width: none; width: 100%; } @@ -589,7 +589,7 @@ line-height: 28px; margin: 0 !important; // Don't cover a product icon - max-width: calc( 100% - 48px ); + max-width: calc(100% - 48px); } .addons-buttons-banner-details h2 { @@ -694,15 +694,15 @@ width: 17px; &__fill { - background-image: url( ../images/icons/star-golden.svg ); + background-image: url(../images/icons/star-golden.svg); } &__half-fill { - background-image: url( ../images/icons/star-half-filled.svg ); + background-image: url(../images/icons/star-half-filled.svg); } &__no-fill { - background-image: url( ../images/icons/star-gray.svg ); + background-image: url(../images/icons/star-gray.svg); } } @@ -755,7 +755,7 @@ .addons-buttons-banner-details-container { padding-left: 0; - width: calc( 100% - 198px - 24px - 24px ); + width: calc(100% - 198px - 24px - 24px); } .addons-buttons-banner-details-container { @@ -782,7 +782,7 @@ .storefront { max-width: 990px; - background: url( ../images/storefront-bg.jpg ) bottom right #f6f6f6; + background: url(../images/storefront-bg.jpg) bottom right #f6f6f6; border: 1px solid #ddd; margin: 1em auto; padding: 24px; @@ -795,7 +795,7 @@ max-width: 400px; height: auto; margin: 0 auto 16px; - box-shadow: 0 1px 6px rgba( 0, 0, 0, 0.1 ); + box-shadow: 0 1px 6px rgba(0, 0, 0, 0.1); } p:last-of-type { @@ -847,7 +847,7 @@ } .current-section-name::after { - transform: rotate( 0.5turn ); + transform: rotate(0.5turn); } } } @@ -904,7 +904,7 @@ button.button-primary { background: #bb77ae; border-color: #a36597; - box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ), 0 1px 0 #a36597; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; color: #fff; text-shadow: 0 -1px 1px #a36597, 1px 0 1px #a36597, 0 1px 1px #a36597, -1px 0 1px #a36597; @@ -915,8 +915,7 @@ &:active { background: #a36597; border-color: #a36597; - box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ), - 0 1px 0 #a36597; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; } } } @@ -970,7 +969,7 @@ white-space: nowrap; } } - > p:not( :last-child ) { + > p:not(:last-child) { width: 85%; } .woocommerce-add-variation-price-container { @@ -1006,6 +1005,14 @@ } } +/** + * Variations related variables + */ +$container-height: 360px; +$max-content-width: 544px; +$default-font-size: 14px; +$default-line-height: 18px; + #variable_product_options { .form-row select { max-width: 100%; @@ -1017,18 +1024,30 @@ } } - .add-attributes-container { + %container-defaults { box-sizing: border-box; display: flex; - align-items: center; padding: 32px 0; - height: 360px; + height: $container-height; + } + + %centered-text { + width: 90%; + max-width: $max-content-width; + font-size: $default-font-size; + line-height: $default-line-height; + text-align: center; + } + + .add-attributes-container { + @extend %container-defaults; + align-items: center; a { text-decoration: none; &[target="_blank"]::after { - content: url('../images/icons/external-link.svg'); + content: url("../images/icons/external-link.svg"); margin-left: 2px; vertical-align: sub; } @@ -1042,14 +1061,33 @@ width: 100%; p { - width: 90%; - max-width: 544px; - font-size: 14px; - line-height: 18px; - text-align: center; + @extend %centered-text; } } } + + .add-variation-container { + @extend %container-defaults; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 24px; + position: relative; + + .arrow-image-wrapper { + position: absolute; + top: 10px; + left: 87px; + } + + p { + @extend %centered-text; + } + + &.hidden { + display: none; + } + } } #product_attributes { @@ -1071,7 +1109,7 @@ padding: 32px 0px; gap: 24px; flex: 1; - @media screen and ( max-width: 782px ) { + @media screen and (max-width: 782px) { button { vertical-align: top; } @@ -1133,7 +1171,7 @@ mark.amount { width: 16px; &::after { - @include icon_dashicons( '\f223' ); + @include icon_dashicons("\f223"); cursor: help; } } @@ -1160,7 +1198,7 @@ table.wc_status_table { margin: 0; } - tr:nth-child( 2n ) { + tr:nth-child(2n) { th, td { background: #fcfcfc; @@ -1311,7 +1349,7 @@ table.wc_status_table--tools { } // Adjust log table columns only when table is not collapsed - @media screen and ( min-width: 783px ) { + @media screen and (min-width: 783px) { .column-timestamp { width: 18%; } @@ -1338,7 +1376,7 @@ table.wc_status_table--tools { #log-viewer { background: #fff; border: 1px solid #e5e5e5; - box-shadow: 0 1px 1px rgba( 0, 0, 0, 0.04 ); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); padding: 5px 20px; pre { @@ -1497,7 +1535,7 @@ ul.wc_coupon_list { */ &::before { - @include icon_dashicons( '\f158' ); + @include icon_dashicons("\f158"); } &:hover::before { @@ -1539,7 +1577,7 @@ ul.wc_coupon_list_block { display: inline-block; &::after { - @include icon_dashicons( '\f345' ); + @include icon_dashicons("\f345"); line-height: 28px; } } @@ -1561,8 +1599,8 @@ ul.wc_coupon_list_block { h2 { margin: 0; - font-family: 'HelveticaNeue-Light', 'Helvetica Neue Light', - 'Helvetica Neue', sans-serif; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", + "Helvetica Neue", sans-serif; font-size: 21px; font-weight: normal; line-height: 1.2; @@ -1586,8 +1624,8 @@ ul.wc_coupon_list_block { p.order_number { margin: 0; - font-family: 'HelveticaNeue-Light', 'Helvetica Neue Light', - 'Helvetica Neue', sans-serif; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", + "Helvetica Neue", sans-serif; font-weight: normal; line-height: 1.6em; font-size: 16px; @@ -1740,7 +1778,7 @@ ul.wc_coupon_list_block { } &::after { - font-family: 'WooCommerce'; + font-family: "WooCommerce"; position: absolute; top: 0; left: 0; @@ -1753,8 +1791,8 @@ ul.wc_coupon_list_block { } a.edit_address::after { - font-family: 'Dashicons'; - content: '\f464'; + font-family: "Dashicons"; + content: "\f464"; } .billing-same-as-shipping, @@ -1886,7 +1924,7 @@ ul.wc_coupon_list_block { margin: 0 0 0 0.5em; box-sizing: border-box; - input[type='text'] { + input[type="text"] { width: 96%; float: right; } @@ -2058,7 +2096,7 @@ ul.wc_coupon_list_block { text-align: center; &:empty::before { - @include icon_dashicons( '\f128' ); + @include icon_dashicons("\f128"); width: 38px; line-height: 38px; display: block; @@ -2129,7 +2167,7 @@ ul.wc_coupon_list_block { display: inline-block; background: #fff; border: 1px solid #ddd; - box-shadow: inset 0 1px 2px rgba( 0, 0, 0, 0.07 ); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07); margin: 1px 0; min-width: 80px; overflow: hidden; @@ -2302,7 +2340,7 @@ ul.wc_coupon_list_block { margin: 0 auto; &::before { - @include icon( '\e007' ); + @include icon("\e007"); color: #ccc; } } @@ -2315,7 +2353,7 @@ ul.wc_coupon_list_block { margin: 0 auto; &::before { - @include icon( '\e014' ); + @include icon("\e014"); color: #ccc; } } @@ -2329,7 +2367,7 @@ ul.wc_coupon_list_block { margin: 0 auto; &::before { - @include icon( '\e01a' ); + @include icon("\e01a"); color: #ccc; } } @@ -2355,7 +2393,7 @@ ul.wc_coupon_list_block { margin: 3px -18px 0 0; &::before { - @include icon_dashicons( '\f153' ); + @include icon_dashicons("\f153"); color: #999; } @@ -2376,7 +2414,7 @@ ul.wc_coupon_list_block { margin-top: 0.5em; &::before { - @include icon_dashicons( '\f171' ); + @include icon_dashicons("\f171"); position: relative; top: auto; left: auto; @@ -2430,14 +2468,14 @@ ul.wc_coupon_list_block { } .edit-order-item::before { - @include icon_dashicons( '\f464' ); + @include icon_dashicons("\f464"); position: relative; } .delete-order-item, .delete_refund { &::before { - @include icon_dashicons( '\f158' ); + @include icon_dashicons("\f158"); position: relative; } @@ -2582,7 +2620,7 @@ ul.wc_coupon_list_block { vertical-align: middle; &::after { - font-family: 'Dashicons'; + font-family: "Dashicons"; speak: never; font-weight: normal; font-variant: normal; @@ -2606,30 +2644,30 @@ ul.wc_coupon_list_block { } a.edit::after { - content: '\f464'; + content: "\f464"; } a.link::after { - font-family: 'WooCommerce'; - content: '\e00d'; + font-family: "WooCommerce"; + content: "\e00d"; } a.view::after { - content: '\f177'; + content: "\f177"; } a.refresh::after { - font-family: 'WooCommerce'; - content: '\e031'; + font-family: "WooCommerce"; + content: "\e031"; } a.processing::after { - font-family: 'WooCommerce'; - content: '\e00f'; + font-family: "WooCommerce"; + content: "\e00f"; } a.complete::after { - content: '\f147'; + content: "\f147"; } } @@ -2720,7 +2758,7 @@ ul.wc_coupon_list_block { border-top: 1px solid #f5f5f5; } - tbody tr:hover:not( .status-trash ):not( .no-link ) td { + tbody tr:hover:not(.status-trash):not(.no-link) td { cursor: pointer; } @@ -2804,7 +2842,7 @@ ul.wc_coupon_list_block { border-radius: 4px; &::before { - @include icon( '\e010' ); + @include icon("\e010"); line-height: 16px; font-size: 14px; vertical-align: middle; @@ -2818,9 +2856,8 @@ ul.wc_coupon_list_block { .order-preview.disabled { &::before { - content: ''; - background: url( '../images/wpspin-2x.gif' ) no-repeat center - top; + content: ""; + background: url("../images/wpspin-2x.gif") no-repeat center top; background-size: 71%; } } @@ -2833,7 +2870,7 @@ ul.wc_coupon_list_block { color: #777; background: #e5e5e5; border-radius: 4px; - border-bottom: 1px solid rgba( 0, 0, 0, 0.05 ); + border-bottom: 1px solid rgba(0, 0, 0, 0.05); margin: -0.25em 0; cursor: inherit !important; white-space: nowrap; @@ -3038,7 +3075,7 @@ ul.wc_coupon_list_block { } } -@media screen and ( max-width: 782px ) { +@media screen and (max-width: 782px) { .wc-order-preview footer { .wc-action-button-group .wc-action-button-group__items { display: flex; @@ -3095,7 +3132,7 @@ ul.wc_coupon_list_block { color: #999; &::after { - @include icon( '\e026' ); + @include icon("\e026"); line-height: 16px; } } @@ -3106,7 +3143,7 @@ ul.wc_coupon_list_block { color: #999; &::after { - @include icon( '\e027' ); + @include icon("\e027"); line-height: 16px; } } @@ -3132,8 +3169,8 @@ ul.wc_coupon_list_block { width: 2em; &::after { - @include icon( '\f111' ); - font-family: 'Dashicons'; + @include icon("\f111"); + font-family: "Dashicons"; line-height: 1.85; } } @@ -3173,7 +3210,7 @@ ul.order_notes { } .note_content::after { - content: ''; + content: ""; display: block; position: absolute; bottom: -10px; @@ -3241,8 +3278,8 @@ table.wp-list-table { vertical-align: text-top; &::before { - content: '\f333'; - font-family: 'Dashicons'; + content: "\f333"; + font-family: "Dashicons"; text-align: center; line-height: 1; color: #999; @@ -3329,17 +3366,17 @@ table.wp-list-table { margin: 0 auto; &::before { - @include icon_dashicons( '\f128' ); + @include icon_dashicons("\f128"); } } span.wc-featured { &::before { - content: '\f155'; + content: "\f155"; } &.not-featured::before { - content: '\f154'; + content: "\f154"; } } @@ -3382,15 +3419,15 @@ table.wp-list-table { } .order-notes_head::after { - content: '\e028'; + content: "\e028"; } .notes_head::after { - content: '\e026'; + content: "\e026"; } .status_head::after { - content: '\e011'; + content: "\e011"; } .column-order_items { @@ -3480,8 +3517,8 @@ table.wc_input_table { background: #fff; cursor: default; - input[type='text'], - input[type='number'] { + input[type="text"], + input[type="number"] { width: 100% !important; min-width: 100px; padding: 8px 10px; @@ -3533,7 +3570,7 @@ table.wc_input_table { padding: 0 4px; } - .ui-sortable:not( .ui-sortable-disabled ) td.sort { + .ui-sortable:not(.ui-sortable-disabled) td.sort { cursor: move; font-size: 15px; background: #f9f9f9; @@ -3541,8 +3578,8 @@ table.wc_input_table { vertical-align: middle; &::before { - content: '\f333'; - font-family: 'Dashicons'; + content: "\f333"; + font-family: "Dashicons"; text-align: center; line-height: 1; color: #999; @@ -3615,7 +3652,7 @@ table.wc_shipping { vertical-align: middle; } - tr:nth-child( odd ) td { + tr:nth-child(odd) td { background: #f9f9f9; } @@ -3650,8 +3687,8 @@ table.wc_shipping { width: 72px; &::before { - content: '\f333'; - font-family: 'Dashicons'; + content: "\f333"; + font-family: "Dashicons"; text-align: center; line-height: 1; color: #999; @@ -3700,11 +3737,11 @@ table.wc_shipping { } .wc-move-down::before { - content: '\f347'; + content: "\f347"; } .wc-move-up::before { - content: '\f343'; + content: "\f343"; } .wc-move-disabled { @@ -3899,8 +3936,8 @@ table.wc-shipping-classes { } &::before { - content: '\e01b'; - font-family: 'WooCommerce'; + content: "\e01b"; + font-family: "WooCommerce"; text-align: center; line-height: 1; color: #eee2ec; @@ -3916,8 +3953,8 @@ table.wc-shipping-classes { .button-primary { background-color: #804877; border-color: #804877; - box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.2 ), - 0 1px 0 rgba( 0, 0, 0, 0.15 ); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), + 0 1px 0 rgba(0, 0, 0, 0.15); margin: 0; opacity: 1; text-shadow: 0 -1px 1px #8a4f7f, 1px 0 1px #8a4f7f, @@ -3931,13 +3968,13 @@ table.wc-shipping-classes { } .wc-shipping-zone-method-rows { - tr:nth-child( even ) td { + tr:nth-child(even) td { background: #f9f9f9; } } tr.odd, - .wc-shipping-class-rows tr:nth-child( odd ) { + .wc-shipping-class-rows tr:nth-child(odd) { td { background: #f9f9f9; } @@ -3974,8 +4011,8 @@ table.wc-shipping-classes { text-align: center; &::before { - content: '\f333'; - font-family: 'Dashicons'; + content: "\f333"; + font-family: "Dashicons"; text-align: center; line-height: 1; color: #999; @@ -3995,8 +4032,8 @@ table.wc-shipping-classes { text-align: center; &::before { - content: '\f319'; - font-family: 'dashicons'; + content: "\f319"; + font-family: "dashicons"; text-align: center; line-height: 1; color: #999; @@ -4057,11 +4094,11 @@ table.wc-shipping-classes { } li::before { - content: ', '; + content: ", "; } li:first-child::before { - content: ''; + content: ""; } } @@ -4075,8 +4112,8 @@ table.wc-shipping-classes { &::before { @include icon; - font-family: 'Dashicons'; - content: '\f502'; + font-family: "Dashicons"; + content: "\f502"; color: #999; vertical-align: middle; line-height: 24px; @@ -4150,7 +4187,7 @@ table.wc-shipping-classes { vertical-align: text-top; &::before { - content: ''; + content: ""; display: block; width: 16px; height: 16px; @@ -4208,7 +4245,7 @@ table.wc-shipping-classes { min-width: 250px; } - input[type='checkbox'] { + input[type="checkbox"] { width: auto; min-width: 16px; } @@ -4270,17 +4307,17 @@ img.help_tip { } .status-manual::before { - @include icon( '\e008' ); + @include icon("\e008"); color: #999; } .status-enabled::before { - @include icon( '\e015' ); + @include icon("\e015"); color: $woocommerce; } .status-disabled::before { - @include icon( '\e013' ); + @include icon("\e013"); color: #ccc; } @@ -4293,7 +4330,7 @@ img.help_tip { margin: 1.5em 0 1em; } - .subsubsub:not( .list-table-filters ) { + .subsubsub:not(.list-table-filters) { margin: -8px 0 0; } @@ -4319,7 +4356,7 @@ img.help_tip { } } - textarea[disabled='disabled'] { + textarea[disabled="disabled"] { background: #dfdfdf !important; } @@ -4336,9 +4373,9 @@ img.help_tip { } } - input[type='text'], - input[type='number'], - input[type='email'] { + input[type="text"], + input[type="number"], + input[type="email"] { height: auto; } @@ -4350,17 +4387,17 @@ img.help_tip { // Give regular settings inputs a standard width and padding. textarea, - input[type='text'], - input[type='email'], - input[type='number'], - input[type='password'], - input[type='datetime'], - input[type='datetime-local'], - input[type='date'], - input[type='time'], - input[type='week'], - input[type='url'], - input[type='tel'], + input[type="text"], + input[type="email"], + input[type="number"], + input[type="password"], + input[type="datetime"], + input[type="datetime-local"], + input[type="date"], + input[type="time"], + input[type="week"], + input[type="url"], + input[type="tel"], input.regular-input { width: 400px; margin: 0; @@ -4369,11 +4406,11 @@ img.help_tip { vertical-align: top; } - input[type='datetime-local'], - input[type='date'], - input[type='time'], - input[type='week'], - input[type='tel'] { + input[type="datetime-local"], + input[type="date"], + input[type="time"], + input[type="week"], + input[type="tel"] { width: 200px; } @@ -4393,9 +4430,9 @@ img.help_tip { table { select, textarea, - input[type='text'], - input[type='email'], - input[type='number'], + input[type="text"], + input[type="email"], + input[type="number"], input.regular-input { width: auto; } @@ -4496,7 +4533,7 @@ img.help_tip { position: absolute; border: 1px solid #ccc; border-radius: 3px; - box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.2 ); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); .ui-slider { border: 0 !important; @@ -4570,27 +4607,27 @@ img.help_tip { table.form-table { // Give regular settings inputs a standard width and padding. textarea, - input[type='text'], - input[type='email'], - input[type='number'], - input[type='password'], - input[type='datetime'], - input[type='datetime-local'], - input[type='date'], - input[type='time'], - input[type='week'], - input[type='url'], - input[type='tel'], + input[type="text"], + input[type="email"], + input[type="number"], + input[type="password"], + input[type="datetime"], + input[type="datetime-local"], + input[type="date"], + input[type="time"], + input[type="week"], + input[type="url"], + input[type="tel"], input.regular-input { padding: 0 8px; - @media only screen and ( max-width: 782px ) { + @media only screen and (max-width: 782px) { width: 100%; } } select { - @media only screen and ( max-width: 782px ) { + @media only screen and (max-width: 782px) { width: 100%; } } @@ -4600,7 +4637,7 @@ img.help_tip { .woocommerce-help-tip { margin: -7px -24px 0 0; - @media only screen and ( max-width: 782px ) { + @media only screen and (max-width: 782px) { right: auto; margin-left: 5px; } @@ -4615,12 +4652,12 @@ img.help_tip { padding: 0; width: 30px; height: 30px; - box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.2 ); + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2); font-size: 16px; border-radius: 4px; margin-right: 3px; - @media only screen and ( max-width: 782px ) { + @media only screen and (max-width: 782px) { float: left; width: 40px; height: 40px; @@ -4714,7 +4751,7 @@ img.help_tip { margin: 9px 9px 0 0; background: #f7f7f7; - @include border-radius( 2px ); + @include border-radius(2px); position: relative; box-sizing: border-box; @@ -4730,7 +4767,7 @@ img.help_tip { position: relative; &::after { - @include icon_dashicons( '\f161' ); + @include icon_dashicons("\f161"); font-size: 2.618em; line-height: 72px; color: #ddd; @@ -4744,7 +4781,7 @@ img.help_tip { padding: 2px; display: none; - @media ( max-width: 768px ) { + @media (max-width: 768px) { display: block; } @@ -4770,7 +4807,7 @@ img.help_tip { font-size: 1.4em; &::before { - @include icon_dashicons( '\f153' ); + @include icon_dashicons("\f153"); color: #999; background: #fff; border-radius: 50%; @@ -4932,7 +4969,7 @@ img.help_tip { box-sizing: border-box; &::after { - content: ''; + content: ""; display: block; width: 100%; height: 9999em; @@ -4964,34 +5001,34 @@ img.help_tip { } &::before { - @include iconbeforedashicons( '\f107' ); + @include iconbeforedashicons("\f107"); } } &.general_options a::before { - content: '\f107'; + content: "\f107"; } &.inventory_options a::before { - content: '\f481'; + content: "\f481"; } &.shipping_options a::before { - font-family: 'WooCommerce'; - content: '\e01a'; + font-family: "WooCommerce"; + content: "\e01a"; } &.linked_product_options a::before { - content: '\f103'; + content: "\f103"; } &.attribute_options a::before { - content: '\f175'; + content: "\f175"; } &.advanced_options a::before { - font-family: 'Dashicons'; - content: '\f111'; + font-family: "Dashicons"; + content: "\f111"; } &.marketplace-suggestions_options a::before { @@ -4999,22 +5036,22 @@ img.help_tip { } &.variations_options a::before { - content: '\f509'; + content: "\f509"; } &.usage_restriction_options a::before { - font-family: 'WooCommerce'; - content: '\e602'; + font-family: "WooCommerce"; + content: "\e602"; } &.usage_limit_options a::before { - font-family: 'WooCommerce'; - content: '\e601'; + font-family: "WooCommerce"; + content: "\e601"; } &.general_coupon_data a::before { - font-family: 'WooCommerce'; - content: '\e600'; + font-family: "WooCommerce"; + content: "\e600"; } &.active a { @@ -5030,8 +5067,8 @@ img.help_tip { * Shipping */ .woocommerce_page_wc-settings { - input[type='url'], - input[type='email'] { + input[type="url"], + input[type="email"] { direction: ltr; } @@ -5045,7 +5082,7 @@ img.help_tip { } .add.button::before { - @include iconbefore( '\e007' ); + @include iconbefore("\e007"); } } @@ -5095,7 +5132,7 @@ img.help_tip { line-height: 24px; &::after { - content: '.'; + content: "."; display: block; height: 0; clear: both; @@ -5165,7 +5202,7 @@ img.help_tip { font-size: 1.2em; &::before { - @include icon_dashicons( '\f153' ); + @include icon_dashicons("\f153"); color: #999; } @@ -5186,8 +5223,8 @@ img.help_tip { padding-right: 7px !important; &::before { - content: '\f333'; - font-family: 'Dashicons'; + content: "\f333"; + font-family: "Dashicons"; text-align: center; line-height: 1; color: #999; @@ -5204,7 +5241,7 @@ img.help_tip { /* Warning asterisk (indicates if there is a problem with a downloadable file). */ span.disabled { - color: var( --wc-red ); + color: var(--wc-red); } } } @@ -5223,8 +5260,8 @@ img.help_tip { vertical-align: middle; &::before { - content: '\f333'; - font-family: 'Dashicons'; + content: "\f333"; + font-family: "Dashicons"; text-align: center; line-height: 28px; color: #999; @@ -5286,7 +5323,7 @@ img.help_tip { margin-bottom: 1em; } - .short:nth-of-type( 2 ) { + .short:nth-of-type(2) { clear: left; } } @@ -5330,10 +5367,10 @@ img.help_tip { vertical-align: top; } - input[type='text'], - input[type='email'], - input[type='number'], - input[type='password'] { + input[type="text"], + input[type="email"], + input[type="number"], + input[type="password"] { width: 50%; float: left; } @@ -5347,10 +5384,10 @@ img.help_tip { float: left; } - input[type='text'].short, - input[type='email'].short, - input[type='number'].short, - input[type='password'].short, + input[type="text"].short, + input[type="email"].short, + input[type="number"].short, + input[type="password"].short, .short { width: 50%; } @@ -5458,7 +5495,7 @@ img.help_tip { margin: 0 !important; border-top: 1px solid white; padding: 9px 12px !important; - &:not( .expand-close-hidden ) { + &:not(.expand-close-hidden) { border-bottom: 1px solid #eee; } @@ -5542,10 +5579,10 @@ img.help_tip { float: right; &::before { - content: '\f142' !important; + content: "\f142" !important; cursor: pointer; display: inline-block; - font: 400 20px/1 'Dashicons'; + font: 400 20px/1 "Dashicons"; line-height: 0.5 !important; padding: 8px 10px; position: relative; @@ -5555,10 +5592,10 @@ img.help_tip { } &.closed { - @include border-radius( 3px ); + @include border-radius(3px); .handlediv::before { - content: '\f140' !important; + content: "\f140" !important; } h3 { @@ -5737,7 +5774,7 @@ img.help_tip { a { padding: 0 10px 3px; - background: rgba( 0, 0, 0, 0.05 ); + background: rgba(0, 0, 0, 0.05); font-size: 16px; font-weight: 400; text-decoration: none; @@ -5748,7 +5785,7 @@ img.help_tip { a.disabled:focus, a.disabled:hover { color: #a0a5aa; - background: rgba( 0, 0, 0, 0.05 ); + background: rgba(0, 0, 0, 0.05); } } @@ -5785,8 +5822,8 @@ img.help_tip { } &::before { - content: '\f128'; - font-family: 'Dashicons'; + content: "\f128"; + font-family: "Dashicons"; position: absolute; top: 0; left: 0; @@ -5805,7 +5842,7 @@ img.help_tip { } &::before { - content: '\f335'; + content: "\f335"; display: none; } @@ -5825,7 +5862,7 @@ img.help_tip { padding: 4px 1em 2px 0; } - input[type='checkbox'] { + input[type="checkbox"] { margin: 0 5px 0 0.5em !important; vertical-align: middle; } @@ -5841,20 +5878,20 @@ img.help_tip { float: right; } - input[type='text'], - input[type='number'], - input[type='password'], - input[type='color'], - input[type='date'], - input[type='datetime'], - input[type='datetime-local'], - input[type='email'], - input[type='month'], - input[type='search'], - input[type='tel'], - input[type='time'], - input[type='url'], - input[type='week'], + input[type="text"], + input[type="number"], + input[type="password"], + input[type="color"], + input[type="date"], + input[type="datetime"], + input[type="datetime-local"], + input[type="email"], + input[type="month"], + input[type="search"], + input[type="tel"], + input[type="time"], + input[type="url"], + input[type="week"], select, textarea { width: 100%; @@ -5974,7 +6011,7 @@ img.tips { text-align: center; border-radius: 3px; padding: 0.618em 1em; - box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.2 ); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); code { padding: 1px; @@ -6009,7 +6046,7 @@ img.tips { z-index: 9999999; &::after { - content: ''; + content: ""; display: block; border: 8px solid #d82223; border-right-color: transparent; @@ -6079,7 +6116,7 @@ img.ui-datepicker-trigger { .postbox { &::after { - content: '.'; + content: "."; display: block; height: 0; clear: both; @@ -6110,7 +6147,7 @@ img.ui-datepicker-trigger { text-decoration: none; &::before { - @include iconbeforedashicons( '\f346' ); + @include iconbeforedashicons("\f346"); margin-right: 4px; } } @@ -6125,7 +6162,7 @@ img.ui-datepicker-trigger { &::before, &::after { - content: ' '; + content: " "; display: table; } @@ -6206,7 +6243,7 @@ img.ui-datepicker-trigger { border: 1px solid #dfdfdf; &::after { - content: '.'; + content: "."; display: block; height: 0; clear: both; @@ -6222,11 +6259,7 @@ img.ui-datepicker-trigger { margin: 0; color: $blue; border-top-width: 0; - background-image: linear-gradient( - to top, - #ececec, - #f9f9f9 - ); + background-image: linear-gradient(to top, #ececec, #f9f9f9); &.section_title:hover { color: $red; @@ -6240,7 +6273,7 @@ img.ui-datepicker-trigger { display: block; &::after { - @include iconafter( '\e035' ); + @include iconafter("\e035"); float: right; font-size: 0.9em; line-height: 1.618; @@ -6364,8 +6397,8 @@ img.ui-datepicker-trigger { color: #464646; font-weight: normal; display: block; - font-family: 'HelveticaNeue-Light', 'Helvetica Neue Light', - 'Helvetica Neue', sans-serif; + font-family: "HelveticaNeue-Light", "Helvetica Neue Light", + "Helvetica Neue", sans-serif; del { color: #e74c3c; @@ -6375,7 +6408,7 @@ img.ui-datepicker-trigger { &:hover { box-shadow: inset 0 -1px 0 0 #dfdfdf, - inset 300px 0 0 rgba( 156, 93, 144, 0.1 ); + inset 300px 0 0 rgba(156, 93, 144, 0.1); border-right: 5px solid #9c5d90 !important; padding-left: 1.5em; color: #9c5d90; @@ -6573,24 +6606,24 @@ table.bar_chart { .woocommerce_page_wc-orders, .post-type-shop_order { .woocommerce-BlankState-message::before { - @include icon( '\e01d' ); + @include icon("\e01d"); } } .post-type-shop_coupon .woocommerce-BlankState-message::before { - @include icon( '\e600' ); + @include icon("\e600"); } .post-type-product .woocommerce-BlankState-message::before { - @include icon( '\e006' ); + @include icon("\e006"); } .woocommerce-BlankState--api .woocommerce-BlankState-message::before { - @include icon( '\e01c' ); + @include icon("\e01c"); } .woocommerce-BlankState--webhooks .woocommerce-BlankState-message::before { - @include icon( '\e01b' ); + @include icon("\e01b"); } .woocommerce-BlankState { @@ -6606,8 +6639,8 @@ table.bar_chart { &::before { color: #ddd; - text-shadow: 0 -1px 1px rgba( 0, 0, 0, 0.2 ), - 0 1px 0 rgba( 255, 255, 255, 0.8 ); + text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.2), + 0 1px 0 rgba(255, 255, 255, 0.8); font-size: 8em; display: block; position: relative !important; @@ -6663,7 +6696,7 @@ table.bar_chart { /** * Small screen optimisation */ -@media only screen and ( max-width: 1280px ) { +@media only screen and (max-width: 1280px) { #order_data { .order_data_column { width: 48%; @@ -6682,10 +6715,10 @@ table.bar_chart { } .short, - input[type='text'].short, - input[type='email'].short, - input[type='number'].short, - input[type='password'].short, + input[type="text"].short, + input[type="email"].short, + input[type="number"].short, + input[type="password"].short, .dimensions_field .wrap { width: 80%; } @@ -6721,7 +6754,7 @@ table.bar_chart { /** * Optimisation for screens 900px and smaller */ -@media only screen and ( max-width: 900px ) { +@media only screen and (max-width: 900px) { #woocommerce-coupon-data ul.coupon_data_tabs, #woocommerce-product-data ul.product_data_tabs, #woocommerce-product-data .wc-tabs-back { @@ -6759,7 +6792,7 @@ table.bar_chart { /** * Optimisation for screens 782px and smaller */ -@media only screen and ( max-width: 782px ) { +@media only screen and (max-width: 782px) { #wp-excerpt-media-buttons a { font-size: 16px; line-height: 37px; @@ -6819,7 +6852,7 @@ table.bar_chart { } } - .is-expanded td:not( .hidden ) { + .is-expanded td:not(.hidden) { overflow: visible; } @@ -6841,7 +6874,7 @@ table.bar_chart { margin: 0; } - .is-expanded td:not( .hidden ) { + .is-expanded td:not(.hidden) { overflow: visible; } @@ -6852,7 +6885,7 @@ table.bar_chart { } } -@media only screen and ( max-width: 500px ) { +@media only screen and (max-width: 500px) { .woocommerce_options_panel label, .woocommerce_options_panel legend { float: none; @@ -6903,7 +6936,7 @@ table.bar_chart { z-index: 100000; left: 50%; top: 50%; - transform: translate( -50%, -50% ); + transform: translate(-50%, -50%); max-width: 100%; min-width: 500px; @@ -6922,7 +6955,7 @@ table.bar_chart { } } -@media screen and ( max-width: 782px ) { +@media screen and (max-width: 782px) { .wc-backbone-modal .wc-backbone-modal-content { width: 100%; height: 100%; @@ -6980,10 +7013,10 @@ table.bar_chart { transition: color 0.1s ease-in-out, background 0.1s ease-in-out; &::before { - font: normal 22px/50px 'dashicons' !important; + font: normal 22px/50px "dashicons" !important; color: #666; display: block; - content: '\f335'; + content: "\f335"; font-weight: 300; } @@ -7072,7 +7105,7 @@ table.bar_chart { padding: 1em 1.5em; background: #fcfcfc; border-top: 1px solid #dfdfdf; - box-shadow: 0 -4px 4px -4px rgba( 0, 0, 0, 0.1 ); + box-shadow: 0 -4px 4px -4px rgba(0, 0, 0, 0.1); .inner { text-align: right; @@ -7114,11 +7147,11 @@ table.bar_chart { } .select2-dropdown--below { - box-shadow: 0 1px 1px rgba( 0, 0, 0, 0.1 ); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); } .select2-dropdown--above { - box-shadow: 0 -1px 1px rgba( 0, 0, 0, 0.1 ); + box-shadow: 0 -1px 1px rgba(0, 0, 0, 0.1); } .select2-container { @@ -7203,12 +7236,12 @@ table.bar_chart { right: 0; height: 1px; background: #fff; - content: ''; + content: ""; } } .select2-dropdown--below { - box-shadow: 0 0 0 1px #007cba, 0 2px 1px rgba( 0, 0, 0, 0.1 ); + box-shadow: 0 0 0 1px #007cba, 0 2px 1px rgba(0, 0, 0, 0.1); &::after { top: -1px; @@ -7216,7 +7249,7 @@ table.bar_chart { } .select2-dropdown--above { - box-shadow: 0 0 0 1px #007cba, 0 -2px 1px rgba( 0, 0, 0, 0.1 ); + box-shadow: 0 0 0 1px #007cba, 0 -2px 1px rgba(0, 0, 0, 0.1); &::after { bottom: -1px; @@ -7224,7 +7257,7 @@ table.bar_chart { } .select2-container { - @media only screen and ( max-width: 782px ) { + @media only screen and (max-width: 782px) { font-size: 16px; } @@ -7236,7 +7269,7 @@ table.bar_chart { height: 30px; border-color: #7e8993; - @media only screen and ( max-width: 782px ) { + @media only screen and (max-width: 782px) { height: 40px; } @@ -7247,7 +7280,7 @@ table.bar_chart { .select2-selection__rendered { line-height: 28px; - @media only screen and ( max-width: 782px ) { + @media only screen and (max-width: 782px) { line-height: 38px; } @@ -7260,11 +7293,11 @@ table.bar_chart { right: 1px; height: 28px; width: 23px; - background: url( 'data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E' ) + background: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E") no-repeat right 5px top 55%; background-size: 16px 16px; - @media only screen and ( max-width: 782px ) { + @media only screen and (max-width: 782px) { height: 38px; } @@ -7294,7 +7327,7 @@ table.bar_chart { } .woocommerce table.form-table .select2-container { - @media only screen and ( max-width: 782px ) { + @media only screen and (max-width: 782px) { min-width: 100% !important; } } @@ -7336,11 +7369,11 @@ table.bar_chart { } .select2-dropdown--below { - box-shadow: 0 0 0 1px $color, 0 2px 1px rgba( 0, 0, 0, 0.1 ); + box-shadow: 0 0 0 1px $color, 0 2px 1px rgba(0, 0, 0, 0.1); } .select2-dropdown--above { - box-shadow: 0 0 0 1px $color, 0 -2px 1px rgba( 0, 0, 0, 0.1 ); + box-shadow: 0 0 0 1px $color, 0 -2px 1px rgba(0, 0, 0, 0.1); } .select2-selection--single .select2-selection__rendered:hover { @@ -7422,7 +7455,7 @@ table.bar_chart { } li::before { - content: ''; + content: ""; border: 4px solid #ccc; border-radius: 100%; width: 4px; @@ -7465,9 +7498,9 @@ table.bar_chart { border-radius: 4px; background-color: #bb77ae; border-color: #a36597; - -webkit-box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ), + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; - box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ), 0 1px 0 #a36597; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; text-shadow: 0 -1px 1px #a36597, 1px 0 1px #a36597, 0 1px 1px #a36597, -1px 0 1px #a36597; margin: 0; @@ -7478,10 +7511,9 @@ table.bar_chart { &:active { background: #a36597; border-color: #a36597; - -webkit-box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ), - 0 1px 0 #a36597; - box-shadow: inset 0 1px 0 rgba( 255, 255, 255, 0.25 ), + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 0 #a36597; } } @@ -7512,7 +7544,7 @@ table.bar_chart { overflow: hidden; padding: 0; margin: 0 0 16px; - box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.13 ); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.13); color: #555; text-align: left; @@ -7563,13 +7595,13 @@ table.bar_chart { font-weight: normal; } - input[type='checkbox'] { + input[type="checkbox"] { margin: 0 4px 0 0; padding: 7px; } - input[type='text'], - input[type='number'] { + input[type="text"], + input[type="number"] { padding: 7px; height: auto; margin: 0; @@ -7577,8 +7609,8 @@ table.bar_chart { .woocommerce-importer-file-url-field-wrapper { border: 1px solid #ddd; - -webkit-box-shadow: inset 0 1px 2px rgba( 0, 0, 0, 0.07 ); - box-shadow: inset 0 1px 2px rgba( 0, 0, 0, 0.07 ); + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07); background-color: #fff; color: #32373c; outline: 0; @@ -7625,7 +7657,7 @@ table.bar_chart { border: 2px solid #eee; border-radius: 4px; padding: 0; - box-shadow: 0 1px 0 0 rgba( 255, 255, 255, 0.2 ); + box-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.2); } progress::-webkit-progress-bar { @@ -7638,25 +7670,25 @@ table.bar_chart { progress::-webkit-progress-value { border-radius: 3px; - box-shadow: inset 0 1px 1px 0 rgba( 255, 255, 255, 0.4 ); + box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4); background: #a46497; - background: linear-gradient( to bottom, #a46497, #66405f ), #a46497; + background: linear-gradient(to bottom, #a46497, #66405f), #a46497; transition: width 1s ease; } progress::-moz-progress-bar { border-radius: 3px; - box-shadow: inset 0 1px 1px 0 rgba( 255, 255, 255, 0.4 ); + box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4); background: #a46497; - background: linear-gradient( to bottom, #a46497, #66405f ), #a46497; + background: linear-gradient(to bottom, #a46497, #66405f), #a46497; transition: width 1s ease; } progress::-ms-fill { border-radius: 3px; - box-shadow: inset 0 1px 1px 0 rgba( 255, 255, 255, 0.4 ); + box-shadow: inset 0 1px 1px 0 rgba(255, 255, 255, 0.4); background: #a46497; - background: linear-gradient( to bottom, #a46497, #66405f ), #a46497; + background: linear-gradient(to bottom, #a46497, #66405f), #a46497; transition: width 1s ease; } @@ -7701,8 +7733,8 @@ table.bar_chart { } } - tbody tr:nth-child( odd ) td, - tbody tr:nth-child( odd ) th { + tbody tr:nth-child(odd) td, + tbody tr:nth-child(odd) th { background: #fbfbfb; } @@ -7748,7 +7780,7 @@ table.bar_chart { line-height: 1.75em; &::before { - @include icon( '\e015' ); + @include icon("\e015"); color: #a16696; position: static; font-size: 100px; @@ -7790,7 +7822,7 @@ table.bar_chart { } } -@media screen and ( min-width: 600px ) { +@media screen and (min-width: 600px) { .wc-addons-wrap { .marketplace-header { padding-left: 84px; @@ -7814,13 +7846,13 @@ table.bar_chart { } } -@media screen and ( min-width: 961px ) { +@media screen and (min-width: 961px) { .marketplace-header__tabs { margin-left: 84px; } } -@media screen and ( min-width: 1024px ) { +@media screen and (min-width: 1024px) { .current-section-name { display: none; } @@ -7895,7 +7927,6 @@ table.bar_chart { * Product Reviews */ .wp-list-table.product-reviews { - .column-author { width: 20%; } @@ -7904,7 +7935,7 @@ table.bar_chart { width: 10%; } - @media screen and ( max-width: 782px ) { + @media screen and (max-width: 782px) { th.column-type, td.column-type, th.column-author, @@ -7955,6 +7986,6 @@ table.bar_chart { } #post-status-info { margin: 0px 12px 12px; - width: calc( 100% - 24px ); + width: calc(100% - 24px); } } diff --git a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js index b4ddb08d186..4d43ad94999 100644 --- a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js +++ b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js @@ -126,7 +126,11 @@ jQuery( function ( $ ) { * @param {Int} qty */ reload: function () { - wc_meta_boxes_product_variations_ajax.load_variations( 1 ); + wc_meta_boxes_product_variations_ajax + .load_variations( 1 ) + .then( + wc_meta_boxes_product_variations_ajax.show_hide_variation_empty_state + ); wc_meta_boxes_product_variations_pagenav.set_paginav( 0 ); }, @@ -655,6 +659,18 @@ jQuery( function ( $ ) { 'a.do_variation_action', this.do_variation_action ); + + $( '.wc-metaboxes-wrapper' ).on( + 'click', + 'button.generate_variations', + this.generate_variations + ); + + $( '.wc-metaboxes-wrapper' ).on( + 'click', + 'button.add_variation_manually', + this.add_variation_manually + ); }, /** @@ -726,41 +742,50 @@ jQuery( function ( $ ) { * @param {Int} per_page (default: 10) */ load_variations: function ( page, per_page ) { - page = page || 1; - per_page = - per_page || - woocommerce_admin_meta_boxes_variations.variations_per_page; + return new Promise( ( resolve, reject ) => { + page = page || 1; + per_page = + per_page || + woocommerce_admin_meta_boxes_variations.variations_per_page; - var wrapper = $( '#variable_product_options' ).find( - '.woocommerce_variations' - ); + var wrapper = $( '#variable_product_options' ).find( + '.woocommerce_variations' + ); - wc_meta_boxes_product_variations_ajax.block(); + wc_meta_boxes_product_variations_ajax.block(); - $.ajax( { - url: woocommerce_admin_meta_boxes_variations.ajax_url, - data: { - action: 'woocommerce_load_variations', - security: - woocommerce_admin_meta_boxes_variations.load_variations_nonce, - product_id: woocommerce_admin_meta_boxes_variations.post_id, - attributes: wrapper.data( 'attributes' ), - page: page, - per_page: per_page, - }, - type: 'POST', - success: function ( response ) { - wrapper - .empty() - .append( response ) - .attr( 'data-page', page ); + $.ajax( { + url: woocommerce_admin_meta_boxes_variations.ajax_url, + data: { + action: 'woocommerce_load_variations', + security: + woocommerce_admin_meta_boxes_variations.load_variations_nonce, + product_id: + woocommerce_admin_meta_boxes_variations.post_id, + attributes: wrapper.data( 'attributes' ), + page: page, + per_page: per_page, + }, + type: 'POST', + success: function ( response ) { + wrapper + .empty() + .append( response ) + .attr( 'data-page', page ); - $( '#woocommerce-product-data' ).trigger( - 'woocommerce_variations_loaded' - ); + $( '#woocommerce-product-data' ).trigger( + 'woocommerce_variations_loaded' + ); - wc_meta_boxes_product_variations_ajax.unblock(); - }, + wc_meta_boxes_product_variations_ajax.unblock(); + + resolve( response ); + }, + error: function ( jqXHR, textStatus, errorThrown ) { + wc_meta_boxes_product_variations_ajax.unblock(); + reject( { jqXHR, textStatus, errorThrown } ); + }, + } ); } ); }, @@ -972,6 +997,7 @@ jQuery( function ( $ ) { 'woocommerce_variations_added', 1 ); + wc_meta_boxes_product_variations_ajax.show_hide_variation_empty_state(); wc_meta_boxes_product_variations_ajax.unblock(); } ); @@ -1104,6 +1130,8 @@ jQuery( function ( $ ) { ); } + wc_meta_boxes_product_variations_ajax.show_hide_variation_empty_state(); + if ( count > 0 ) { wc_meta_boxes_product_variations_pagenav.go_to_page( 1, @@ -1173,12 +1201,6 @@ jQuery( function ( $ ) { value; switch ( do_variation_action ) { - case 'add_variation': - wc_meta_boxes_product_variations_ajax.add_variation(); - return; - case 'link_all_variations': - wc_meta_boxes_product_variations_ajax.link_all_variations(); - return; case 'delete_all': if ( window.confirm( @@ -1311,6 +1333,38 @@ jQuery( function ( $ ) { }, } ); }, + + /** + * Show/hide variation empty state + */ + show_hide_variation_empty_state: function () { + var wrapper = $( '#variable_product_options' ).find( + '.woocommerce_variations' + ); + if ( parseInt( wrapper.attr( 'data-total' ) ) > 0 ) { + $( '.add-variation-container' ).addClass( 'hidden' ); + $( '#field_to_edit' ).removeClass( 'hidden' ); + $( 'a.do_variation_action' ).removeClass( 'hidden' ); + } else { + $( '.add-variation-container' ).removeClass( 'hidden' ); + $( '#field_to_edit' ).addClass( 'hidden' ); + $( 'a.do_variation_action' ).addClass( 'hidden' ); + } + }, + + /** + * Generate variations + */ + generate_variations: function () { + wc_meta_boxes_product_variations_ajax.link_all_variations(); + }, + + /** + * Add variation + */ + add_variation_manually: function () { + wc_meta_boxes_product_variations_ajax.add_variation(); + }, }; /** @@ -1397,7 +1451,7 @@ jQuery( function ( $ ) { if ( page_nav.is( ':hidden' ) ) { $( 'option, optgroup', '.variation_actions' ).show(); - $( '.variation_actions' ).val( 'add_variation' ); + $( '.variation_actions' ).val( 'delete_all' ); $( '#variable_product_options' ).find( '.toolbar' ).show(); page_nav.show(); $( '.pagination-links', page_nav ).hide(); @@ -1444,13 +1498,13 @@ jQuery( function ( $ ) { toolbar.not( '.toolbar-top, .toolbar-buttons' ).hide(); page_nav.hide(); $( 'option, optgroup', variation_action ).hide(); - $( '.variation_actions' ).val( 'add_variation' ); + $( '.variation_actions' ).val( 'delete_all' ); $( 'option[data-global="true"]', variation_action ).show(); } else { toolbar.show(); page_nav.show(); $( 'option, optgroup', variation_action ).show(); - $( '.variation_actions' ).val( 'add_variation' ); + $( '.variation_actions' ).val( 'delete_all' ); // Show/hide links if ( 1 === total_pages ) { @@ -1536,7 +1590,11 @@ jQuery( function ( $ ) { selected, parseInt( wrapper.attr( 'data-total_pages' ), 10 ) ); - wc_meta_boxes_product_variations_ajax.load_variations( selected ); + wc_meta_boxes_product_variations_ajax + .load_variations( selected ) + .then( + wc_meta_boxes_product_variations_ajax.show_hide_variation_empty_state() + ); }, /** diff --git a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php index 1b85eb444b3..06507a54061 100644 --- a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php +++ b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php @@ -9,7 +9,9 @@ if ( ! defined( 'ABSPATH' ) ) { exit; } -$add_attributes_icon_url = WC_ADMIN_IMAGES_FOLDER_URL . '/icons/info.svg'; +$add_attributes_img_url = WC_ADMIN_IMAGES_FOLDER_URL . '/icons/info.svg'; +$background_img_url = WC_ADMIN_IMAGES_FOLDER_URL . '/product_data/no-variation-background-image.svg'; +$arrow_img_url = WC_ADMIN_IMAGES_FOLDER_URL . '/product_data/no-variation-arrow.svg'; ?>

  • - + Date: Mon, 3 Apr 2023 12:51:23 -0400 Subject: [PATCH 112/466] Updating core pull request template (#37555) Streamlining the pull request template by removing checkboxes --- .github/PULL_REQUEST_TEMPLATE.md | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3b1ff6841c6..0ff2173615b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,10 +1,9 @@ -### All Submissions: +### Submission Review Guidelines: -- [ ] Have you followed the [WooCommerce Contributing guideline](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md)? -- [ ] Does your code follow the [WordPress' coding standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/)? -- [ ] Have you checked to ensure there aren't other open [Pull Requests](https://github.com/woocommerce/woocommerce/pulls) for the same update/change? - - +- I have followed the [WooCommerce Contributing Guidelines](https://github.com/woocommerce/woocommerce/blob/trunk/.github/CONTRIBUTING.md) and the [WordPress Coding Standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/). +- I have checked to ensure there aren't other open [Pull Requests](https://github.com/woocommerce/woocommerce/pulls) for the same update/change. +- I have reviewed my code for [security best practices](https://developer.wordpress.org/apis/security/). +- Following the above guidelines will result in quick merges and clear and detailed feedback when appropriate. @@ -18,25 +17,12 @@ Closes # . ### How to test the changes in this Pull Request: - + -- [ ] Have you followed the [Writing high-quality testing instructions guide](https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions)? +Using the [WooCommerce Testing Instructions Guide](https://github.com/woocommerce/woocommerce/wiki/Writing-high-quality-testing-instructions), include your detailed testing instructions: 1. 2. 3. - - -### Other information: - -- [ ] Have you added an explanation of what your changes do and why you'd like us to include them? -- [ ] Have you written new tests for your changes, as applicable? -- [ ] Have you created a changelog file for each project being changed, ie `pnpm --filter= changelog add`? -- [ ] Have you included testing instructions? - - - -### FOR PR REVIEWER ONLY: - -- [ ] I have reviewed that everything is sanitized/escaped appropriately for any SQL or XSS injection possibilities. I made sure Linting is not ignored or disabled. + \ No newline at end of file From 1384a54a98a5e112c945068137836d1cf2a59db8 Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:17:33 -0700 Subject: [PATCH 113/466] Correct development blog link. --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3e3b424196e..7421d61af09 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -7,7 +7,7 @@ There are many ways to contribute to the project! - [Translating strings into your language](https://github.com/woocommerce/woocommerce/wiki/Translating-WooCommerce). - Answering questions on the various WooCommerce communities like the [WP.org support forums](https://wordpress.org/support/plugin/woocommerce/). - Testing open [issues](https://github.com/woocommerce/woocommerce/issues) or [pull requests](https://github.com/woocommerce/woocommerce/pulls) and sharing your findings in a comment. -- Testing WooCommerce beta versions and release candidates. Those are announced in the [WooCommerce development blog](https://woocommerce.wordpress.com/). +- Testing WooCommerce beta versions and release candidates. Those are announced in the [WooCommerce development blog](https://developer.woocommerce.com/blog/). - Submitting fixes, improvements, and enhancements. - To disclose a security issue to our team, [please submit a report via HackerOne](https://hackerone.com/automattic/). From 5d0cf7e84481121e6182574077784c7df8734d6f Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:20:53 -0700 Subject: [PATCH 114/466] Update links to GitHub articles. --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 7421d61af09..a194a64876e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -11,7 +11,7 @@ There are many ways to contribute to the project! - Submitting fixes, improvements, and enhancements. - To disclose a security issue to our team, [please submit a report via HackerOne](https://hackerone.com/automattic/). -If you wish to contribute code, please read the information in the sections below. Then [fork](https://help.github.com/articles/fork-a-repo/) WooCommerce, commit your changes, and [submit a pull request](https://help.github.com/articles/using-pull-requests/) 🎉 +If you wish to contribute code, please read the information in the sections below. Then [fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) WooCommerce, commit your changes, and [submit a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) 🎉 We use the `good first issue` label to mark issues that are suitable for new contributors. You can find all the issues with this label [here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+good+first+issue%22). From 766aaea48e5b3677c500eb815aa7bbdb2d9a3652 Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:28:17 -0700 Subject: [PATCH 115/466] No need for a one-item bulleted list. --- .github/CONTRIBUTING.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index a194a64876e..ecde5853704 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -41,9 +41,8 @@ If you have questions about the process to contribute code or want to discuss de - Make sure to write good and detailed commit messages (see [this post](https://chris.beams.io/posts/git-commit/) for more on this) and follow all the applicable sections of the pull request template. - Please avoid modifying the changelog directly or updating the .pot files. These will be updated by the WooCommerce team. -If you are contributing code to the (Javascript-driven) Gutenberg blocks, note that it's developed in an external package. - -- [Blocks](https://github.com/woocommerce/woocommerce-gutenberg-products-block) +If you are contributing code to our (Javascript-driven) Gutenberg blocks, please note that +they are developed in their [own repository](https://github.com/woocommerce/woocommerce-gutenberg-products-block) and have their [own issue tracker](https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues). ## Feature Requests 🚀 From 79cda9aa297b267c10e35f9c47eafa8aca99f5af Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:29:03 -0700 Subject: [PATCH 116/466] Revise feature request guidance. --- .github/CONTRIBUTING.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ecde5853704..89336e7ac00 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -46,8 +46,6 @@ they are developed in their [own repository](https://github.com/woocommerce/wooc ## Feature Requests 🚀 -Feature requests can be [submitted to our issue tracker](https://github.com/woocommerce/woocommerce/issues/new?assignees=&labels=type%3A+enhancement%2Cstatus%3A+awaiting+triage&template=2-enhancement.yml&title=%5BEnhancement%5D%3A+). Be sure to include a description of the expected behavior and use case, and before submitting a request, please search for similar ones in the closed issues. +The best place to submit feature requests is over on our [dedicated feature request page](https://woocommerce.com/feature-requests/woocommerce/). You can easily search and vote for existing requests, or create new requests if necessary. -Feature request issues will remain closed until we see sufficient interest via comments and [👍 reactions](https://help.github.com/articles/about-discussions-in-issues-and-pull-requests/) from the community. - -You can see a [list of current feature requests which require votes here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+sort%3Areactions-%2B1-desc+label%3A%22needs%3A+votes%22+). +Alternatively, if you wish to propose a straightforward technical enhancement that is unlikely to require much discussion, you can [open a new issue](https://github.com/woocommerce/woocommerce/issues/new?assignees=&labels=type%3A+enhancement%2Cstatus%3A+awaiting+triage&template=2-enhancement.yml&title=%5BEnhancement%5D%3A+) right here on GitHub. From 67570fd420ac988a18d5994576d6a75c3b35e5e3 Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:33:13 -0700 Subject: [PATCH 117/466] Punctuation. --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 89336e7ac00..e0f75e2b3e1 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -31,7 +31,7 @@ If you have questions about the process to contribute code or want to discuss de ## Coding Guidelines and Development 🛠 -- Ensure you stick to the [WordPress Coding Standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/) +- Ensure you stick to the [WordPress Coding Standards](https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/). - Run our build process described in the document on [how to set up WooCommerce development environment](https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment), it will install our pre-commit hook, code sniffs, dependencies, and more. - Whenever possible please fix pre-existing code standards errors in the files that you change. It is ok to skip that for larger files or complex fixes. - Ensure you use LF line endings in your code editor. Use [EditorConfig](http://editorconfig.org/) if your editor supports it so that indentation, line endings and other settings are auto configured. From 25dc06dbbe527d703b924d43bc77c25a0a2cf746 Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 3 Apr 2023 11:37:26 -0700 Subject: [PATCH 118/466] Clarify changelog procedure. --- .github/CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e0f75e2b3e1..3315bd462b8 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -39,7 +39,8 @@ If you have questions about the process to contribute code or want to discuss de - Ensure that your code supports the minimum supported versions of PHP and WordPress; this is shown at the top of the `readme.txt` file. - Push the changes to your fork and submit a pull request on the trunk branch of the WooCommerce repository. - Make sure to write good and detailed commit messages (see [this post](https://chris.beams.io/posts/git-commit/) for more on this) and follow all the applicable sections of the pull request template. -- Please avoid modifying the changelog directly or updating the .pot files. These will be updated by the WooCommerce team. +- Please add a changelog entry by following the steps detailed in the [development guide](https://github.com/woocommerce/woocommerce/blob/trunk/DEVELOPMENT.md), but do not modify `changelog.txt` directly. +- Please do not update any `.pot` files. These will be updated by the WooCommerce team. If you are contributing code to our (Javascript-driven) Gutenberg blocks, please note that they are developed in their [own repository](https://github.com/woocommerce/woocommerce-gutenberg-products-block) and have their [own issue tracker](https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues). From b156651243bf611d9dc30592c2a93ad444efe038 Mon Sep 17 00:00:00 2001 From: "Jorge A. Torres" Date: Mon, 3 Apr 2023 13:55:30 -0500 Subject: [PATCH 119/466] Use backticks for JOIN on OrdersTableQuery --- .../src/Internal/DataStores/Orders/OrdersTableQuery.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableQuery.php b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableQuery.php index 92148b218c6..f62dff69b4e 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableQuery.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableQuery.php @@ -755,9 +755,9 @@ class OrdersTableQuery { if ( empty( $on ) ) { if ( $this->tables['orders'] === $table ) { - $on = "{$this->tables['orders']}.id = {$alias}.id"; + $on = "`{$this->tables['orders']}`.id = `{$alias}`.id"; } else { - $on = "{$this->tables['orders']}.id = {$alias}.order_id"; + $on = "`{$this->tables['orders']}`.id = `{$alias}`.order_id"; } } @@ -775,8 +775,8 @@ class OrdersTableQuery { } $sql_join = ''; - $sql_join .= "{$join_type} JOIN {$table} "; - $sql_join .= ( $alias !== $table ) ? "AS {$alias} " : ''; + $sql_join .= "{$join_type} JOIN `{$table}` "; + $sql_join .= ( $alias !== $table ) ? "AS `{$alias}` " : ''; $sql_join .= "ON ( {$on} )"; $this->join[ $alias ] = $sql_join; From 35bbb0c9206708a7eed4e8fc8c4a070c1a114ddb Mon Sep 17 00:00:00 2001 From: "Jorge A. Torres" Date: Mon, 3 Apr 2023 13:55:38 -0500 Subject: [PATCH 120/466] Re-use aliases from OrdersTableQuery for field queries --- .../src/Internal/DataStores/Orders/OrdersTableFieldQuery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableFieldQuery.php b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableFieldQuery.php index 83bee65d254..bc77c5a30fe 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableFieldQuery.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableFieldQuery.php @@ -246,7 +246,7 @@ class OrdersTableFieldQuery { $this->table_aliases[] = $alias; if ( $join ) { - $this->join[] = $join; + $this->join[ $alias ] = $join; } return $alias; From 2cd5cdcc8c2c423dd0d72f718d1e87aa76441e4b Mon Sep 17 00:00:00 2001 From: "Jorge A. Torres" Date: Mon, 3 Apr 2023 13:56:27 -0500 Subject: [PATCH 121/466] Add changelog --- plugins/woocommerce/changelog/fix-37173 | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/fix-37173 diff --git a/plugins/woocommerce/changelog/fix-37173 b/plugins/woocommerce/changelog/fix-37173 new file mode 100644 index 00000000000..a8e167ce10b --- /dev/null +++ b/plugins/woocommerce/changelog/fix-37173 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix table alias issue in order field queries. From ac343d08d3c35b7901c682a7c76e12a25fdd1e09 Mon Sep 17 00:00:00 2001 From: jonathansadowski Date: Mon, 3 Apr 2023 13:57:27 -0500 Subject: [PATCH 122/466] Add info about changelog tool to contributor guide (#37556) * Add info about changelog tool to contributor guide * Update .github/CONTRIBUTING.md Co-authored-by: Leif Singer --------- Co-authored-by: Leif Singer --- .github/CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3e3b424196e..5dfdfc34c42 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -39,7 +39,8 @@ If you have questions about the process to contribute code or want to discuss de - Ensure that your code supports the minimum supported versions of PHP and WordPress; this is shown at the top of the `readme.txt` file. - Push the changes to your fork and submit a pull request on the trunk branch of the WooCommerce repository. - Make sure to write good and detailed commit messages (see [this post](https://chris.beams.io/posts/git-commit/) for more on this) and follow all the applicable sections of the pull request template. -- Please avoid modifying the changelog directly or updating the .pot files. These will be updated by the WooCommerce team. +- Please create a change file for your changes by running `pnpm --filter= changelog add`. For example, a change file for the WooCommerce Core project would be added by running `pnpm --filter=woocommerce changelog add`. +- Please avoid modifying the changelog directly or updating the .pot files. These will be updated by the WooCommerce team. If you are contributing code to the (Javascript-driven) Gutenberg blocks, note that it's developed in an external package. From 56c4741757ec13ed301909da5209c159b25d38eb Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Mon, 3 Apr 2023 12:01:07 -0700 Subject: [PATCH 123/466] Formatting. --- .github/CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3315bd462b8..94ac442fe9c 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -42,8 +42,7 @@ If you have questions about the process to contribute code or want to discuss de - Please add a changelog entry by following the steps detailed in the [development guide](https://github.com/woocommerce/woocommerce/blob/trunk/DEVELOPMENT.md), but do not modify `changelog.txt` directly. - Please do not update any `.pot` files. These will be updated by the WooCommerce team. -If you are contributing code to our (Javascript-driven) Gutenberg blocks, please note that -they are developed in their [own repository](https://github.com/woocommerce/woocommerce-gutenberg-products-block) and have their [own issue tracker](https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues). +If you are contributing code to our (Javascript-driven) Gutenberg blocks, please note that they are developed in their [own repository](https://github.com/woocommerce/woocommerce-gutenberg-products-block) and have their [own issue tracker](https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues). ## Feature Requests 🚀 From 6291d10112c9467f57c5b0df655b694fde08d730 Mon Sep 17 00:00:00 2001 From: Nathan Silveira Date: Tue, 4 Apr 2023 09:28:19 -0300 Subject: [PATCH 124/466] Use SelectTree component in Category field (#37479) * Migrate category field to TreeSelect * Rename and refactor some props due to changes in SelectTree that were introduced afterwards * add Changelog * Rename type to a better name * Add tests to select-tree and remove tests from category-field * Restore ts change that broke build * Run linter * Fix broken tests * Update test * Import from wordpress instead of react --- .../changelog/update-migrate-category-field | 5 + .../test/select-tree.test.tsx | 107 +++++++ .../changelog/update-migrate-category-field | 4 + .../category-field-item.tsx | 12 +- .../category-field.tsx | 250 ++++++--------- .../create-category-modal.tsx | 10 +- .../details-categories-field.tsx | 7 +- .../test/category-field.test.tsx | 284 ++---------------- .../use-category-search.ts | 34 ++- 9 files changed, 263 insertions(+), 450 deletions(-) create mode 100644 packages/js/components/changelog/update-migrate-category-field create mode 100644 packages/js/components/src/experimental-select-tree-control/test/select-tree.test.tsx create mode 100644 packages/js/product-editor/changelog/update-migrate-category-field diff --git a/packages/js/components/changelog/update-migrate-category-field b/packages/js/components/changelog/update-migrate-category-field new file mode 100644 index 00000000000..6c8e15c88a6 --- /dev/null +++ b/packages/js/components/changelog/update-migrate-category-field @@ -0,0 +1,5 @@ +Significance: patch +Type: dev +Comment: Add unit tests + + diff --git a/packages/js/components/src/experimental-select-tree-control/test/select-tree.test.tsx b/packages/js/components/src/experimental-select-tree-control/test/select-tree.test.tsx new file mode 100644 index 00000000000..efb87059c6c --- /dev/null +++ b/packages/js/components/src/experimental-select-tree-control/test/select-tree.test.tsx @@ -0,0 +1,107 @@ +import { render } from '@testing-library/react'; +import React, { createElement } from '@wordpress/element'; +import { SelectTree } from '../select-tree'; +import { Item } from '../../experimental-tree-control'; + +const mockItems: Item[] = [ + { + label: 'Item 1', + value: 'item-1', + }, + { + label: 'Item 2', + value: 'item-2', + parent: 'item-1', + }, + { + label: 'Item 3', + value: 'item-3', + }, +]; + +const DEFAULT_PROPS = { + id: 'select-tree', + items: mockItems, + label: 'Select Tree', + placeholder: 'Type here', +}; + +describe( 'SelectTree', () => { + beforeEach( () => { + jest.clearAllMocks(); + } ); + + it( 'should show the popover only when focused', () => { + const { queryByPlaceholderText, queryByText } = render( + + ); + expect( queryByText( 'Item 1' ) ).not.toBeInTheDocument(); + queryByPlaceholderText( 'Type here' )?.focus(); + expect( queryByText( 'Item 1' ) ).toBeInTheDocument(); + } ); + + it( 'should show create button when callback is true ', () => { + const { queryByText, queryByPlaceholderText } = render( + true } + /> + ); + queryByPlaceholderText( 'Type here' )?.focus(); + expect( queryByText( 'Create new' ) ).toBeInTheDocument(); + } ); + it( 'should not show create button when callback is false or no callback', () => { + const { queryByText, queryByPlaceholderText } = render( + + ); + queryByPlaceholderText( 'Type here' )?.focus(); + expect( queryByText( 'Create new' ) ).not.toBeInTheDocument(); + } ); + it( 'should show a root item when focused and child when expand button is clicked', () => { + const { queryByText, queryByLabelText, queryByPlaceholderText } = + render( ); + queryByPlaceholderText( 'Type here' )?.focus(); + expect( queryByText( 'Item 1' ) ).toBeInTheDocument(); + + expect( queryByText( 'Item 2' ) ).not.toBeInTheDocument(); + queryByLabelText( 'Expand' )?.click(); + expect( queryByText( 'Item 2' ) ).toBeInTheDocument(); + } ); + + it( 'should show selected items', () => { + const { queryAllByRole, queryByPlaceholderText } = render( + + ); + queryByPlaceholderText( 'Type here' )?.focus(); + expect( queryAllByRole( 'treeitem' )[ 0 ] ).toHaveAttribute( + 'aria-selected', + 'true' + ); + } ); + + it( 'should show Create "" button', () => { + const { queryByPlaceholderText, queryByText } = render( + true } + /> + ); + queryByPlaceholderText( 'Type here' )?.focus(); + expect( queryByText( 'Create "new item"' ) ).toBeInTheDocument(); + } ); + it( 'should call onCreateNew when Create "" button is clicked', () => { + const mockFn = jest.fn(); + const { queryByPlaceholderText, queryByText } = render( + true } + onCreateNew={ mockFn } + /> + ); + queryByPlaceholderText( 'Type here' )?.focus(); + queryByText( 'Create "new item"' )?.click(); + expect( mockFn ).toBeCalledTimes( 1 ); + } ); +} ); diff --git a/packages/js/product-editor/changelog/update-migrate-category-field b/packages/js/product-editor/changelog/update-migrate-category-field new file mode 100644 index 00000000000..06c9e04e9a2 --- /dev/null +++ b/packages/js/product-editor/changelog/update-migrate-category-field @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Use SelectTree component in product editor's category field diff --git a/packages/js/product-editor/src/components/details-categories-field/category-field-item.tsx b/packages/js/product-editor/src/components/details-categories-field/category-field-item.tsx index 99ae2d273a3..689554220fd 100644 --- a/packages/js/product-editor/src/components/details-categories-field/category-field-item.tsx +++ b/packages/js/product-editor/src/components/details-categories-field/category-field-item.tsx @@ -9,6 +9,11 @@ import { ProductCategory } from '@woocommerce/data'; import { __experimentalSelectControlMenuItemProps as MenuItemProps } from '@woocommerce/components'; import classNames from 'classnames'; +/** + * Internal dependencies + */ +import { ProductCategoryNode } from './use-category-search'; + export type CategoryTreeItem = { data: ProductCategory; children: CategoryTreeItem[]; @@ -19,13 +24,10 @@ export type CategoryTreeItem = { type CategoryFieldItemProps = { item: CategoryTreeItem; selectedIds: number[]; - items: Pick< ProductCategory, 'id' | 'name' >[]; + items: ProductCategoryNode[]; highlightedIndex: number; openParent?: () => void; -} & Pick< - MenuItemProps< Pick< ProductCategory, 'id' | 'name' > >, - 'getItemProps' ->; +} & Pick< MenuItemProps< ProductCategoryNode >, 'getItemProps' >; export const CategoryFieldItem: React.FC< CategoryFieldItemProps > = ( { item, diff --git a/packages/js/product-editor/src/components/details-categories-field/category-field.tsx b/packages/js/product-editor/src/components/details-categories-field/category-field.tsx index e8c8fe1b626..f9160e19180 100644 --- a/packages/js/product-editor/src/components/details-categories-field/category-field.tsx +++ b/packages/js/product-editor/src/components/details-categories-field/category-field.tsx @@ -3,11 +3,8 @@ */ import { useMemo, useState, createElement, Fragment } from '@wordpress/element'; import { - selectControlStateChangeTypes, - Spinner, - __experimentalSelectControl as SelectControl, - __experimentalSelectControlMenuSlot as MenuSlot, - __experimentalSelectControlMenu as Menu, + TreeItemType, + __experimentalSelectTreeControl as SelectTree, } from '@woocommerce/components'; import { ProductCategory } from '@woocommerce/data'; import { debounce } from 'lodash'; @@ -15,16 +12,15 @@ import { debounce } from 'lodash'; /** * Internal dependencies */ -import { CategoryFieldItem, CategoryTreeItem } from './category-field-item'; -import { useCategorySearch } from './use-category-search'; +import { CategoryTreeItem } from './category-field-item'; +import { useCategorySearch, ProductCategoryNode } from './use-category-search'; import { CreateCategoryModal } from './create-category-modal'; -import { CategoryFieldAddNewItem } from './category-field-add-new-item'; type CategoryFieldProps = { label: string; placeholder: string; - value?: Pick< ProductCategory, 'id' | 'name' >[]; - onChange: ( value: Pick< ProductCategory, 'id' | 'name' >[] ) => void; + value?: ProductCategoryNode[]; + onChange: ( value: ProductCategoryNode[] ) => void; }; /** @@ -32,11 +28,11 @@ type CategoryFieldProps = { * if not included already. */ function getSelectedWithParents( - selected: Pick< ProductCategory, 'id' | 'name' >[] = [], + selected: ProductCategoryNode[] = [], item: ProductCategory, treeKeyValues: Record< number, CategoryTreeItem > -): Pick< ProductCategory, 'id' | 'name' >[] { - selected.push( { id: item.id, name: item.name } ); +): ProductCategoryNode[] { + selected.push( { id: item.id, name: item.name, parent: item.parent } ); const parentId = item.parent !== undefined @@ -59,6 +55,33 @@ function getSelectedWithParents( return selected; } +function mapFromCategoryType( + categories: ProductCategoryNode[] +): TreeItemType[] { + return categories.map( ( val ) => + val.parent + ? { + value: String( val.id ), + label: val.name, + parent: String( val.parent ), + } + : { + value: String( val.id ), + label: val.name, + } + ); +} + +function mapToCategoryType( + categories: TreeItemType[] +): ProductCategoryNode[] { + return categories.map( ( cat ) => ( { + id: +cat.value, + name: cat.label, + parent: cat.parent ? +cat.parent : 0, + } ) ); +} + export const CategoryField: React.FC< CategoryFieldProps > = ( { label, placeholder, @@ -70,7 +93,7 @@ export const CategoryField: React.FC< CategoryFieldProps > = ( { categoriesSelectList, categoryTreeKeyValues, searchCategories, - getFilteredItems, + getFilteredItemsForSelectTree, } = useCategorySearch(); const [ showCreateNewModal, setShowCreateNewModal ] = useState( false ); const [ searchValue, setSearchValue ] = useState( '' ); @@ -85,167 +108,62 @@ export const CategoryField: React.FC< CategoryFieldProps > = ( { [ onInputChange ] ); - const onSelect = ( itemId: number, selected: boolean ) => { - if ( itemId === -99 ) { - setShowCreateNewModal( true ); - return; - } - if ( selected ) { - const item = categoryTreeKeyValues[ itemId ].data; - if ( item ) { - onChange( - getSelectedWithParents( - [ ...value ], - item, - categoryTreeKeyValues - ) - ); - } - } else { - onChange( value.filter( ( i ) => i.id !== itemId ) ); - } - }; - - const categoryFieldGetFilteredItems = ( - allItems: Pick< ProductCategory, 'id' | 'name' >[], - inputValue: string, - selectedItems: Pick< ProductCategory, 'id' | 'name' >[] - ) => { - const filteredItems = getFilteredItems( - allItems, - inputValue, - selectedItems - ); - if ( - inputValue.length > 0 && - ! isSearching && - ! filteredItems.find( - ( item ) => item.name.toLowerCase() === inputValue.toLowerCase() - ) - ) { - return [ - ...filteredItems, - { - id: -99, - name: inputValue, - }, - ]; - } - return filteredItems; - }; - - const selectedIds = value.map( ( item ) => item.id ); - return ( <> - > - className="woocommerce-category-field-dropdown components-base-control" + item?.name || '' } - getItemValue={ ( item ) => item?.id || '' } - onSelect={ ( item ) => { - if ( item ) { - onSelect( item.id, ! selectedIds.includes( item.id ) ); - } - } } - onRemove={ ( item ) => item && onSelect( item.id, false ) } + isLoading={ isSearching } onInputChange={ searchDelayed } - getFilteredItems={ categoryFieldGetFilteredItems } placeholder={ value.length === 0 ? placeholder : '' } - stateReducer={ ( state, actionAndChanges ) => { - const { changes, type } = actionAndChanges; - switch ( type ) { - case selectControlStateChangeTypes.ControlledPropUpdatedSelectedItem: - return { - ...changes, - inputValue: state.inputValue, - }; - case selectControlStateChangeTypes.ItemClick: - if ( - changes.selectedItem && - changes.selectedItem.id === -99 - ) { - return changes; - } - return { - ...changes, - isOpen: true, - inputValue: state.inputValue, - highlightedIndex: state.highlightedIndex, - }; - default: - return changes; + onCreateNew={ () => { + setShowCreateNewModal( true ); + } } + shouldShowCreateButton={ ( typedValue ) => + ! typedValue || + categoriesSelectList.findIndex( + ( item ) => item.name === typedValue + ) === -1 + } + items={ getFilteredItemsForSelectTree( + mapFromCategoryType( categoriesSelectList ), + searchValue, + mapFromCategoryType( value ) + ) } + selected={ mapFromCategoryType( value ) } + onSelect={ ( selectedItems ) => { + if ( Array.isArray( selectedItems ) ) { + const newItems: ProductCategoryNode[] = + mapToCategoryType( + selectedItems.filter( + ( { value: selectedItemValue } ) => + ! value.some( + ( item ) => + item.id === +selectedItemValue + ) + ) + ); + onChange( [ ...value, ...newItems ] ); } } } - __experimentalOpenMenuOnFocus - > - { ( { - items, - isOpen, - getMenuProps, - getItemProps, - highlightedIndex, - } ) => { - const rootItems = - items.length > 0 - ? items.filter( - ( item ) => - categoryTreeKeyValues[ item.id ] - ?.parentID === 0 || item.id === -99 - ) - : []; - return ( - - <> - { isSearching ? ( -
  • -
    - -
    -
  • - ) : ( - rootItems.map( ( item ) => { - return item.id === -99 ? ( - - ) : ( - - ); - } ) - ) } - -
    - ); + onRemove={ ( removedItems ) => { + const newValues = Array.isArray( removedItems ) + ? value.filter( + ( item ) => + ! removedItems.some( + ( { value: removedValue } ) => + item.id === +removedValue + ) + ) + : value.filter( + ( item ) => item.id !== +removedItems.value + ); + onChange( newValues ); } } - - + >
    { showCreateNewModal && ( = ( { const [ categoryName, setCategoryName ] = useState( initialCategoryName || '' ); - const [ categoryParent, setCategoryParent ] = useState< Pick< - ProductCategory, - 'id' | 'name' - > | null >( null ); + const [ categoryParent, setCategoryParent ] = + useState< ProductCategoryNode | null >( null ); const onSave = async () => { recordEvent( 'product_category_add', { @@ -94,7 +92,7 @@ export const CreateCategoryModal: React.FC< CreateCategoryModalProps > = ( { value={ categoryName } onChange={ setCategoryName } /> - > + items={ categoriesSelectList } label={ createInterpolateElement( __( 'Parent category ', 'woocommerce' ), diff --git a/packages/js/product-editor/src/components/details-categories-field/details-categories-field.tsx b/packages/js/product-editor/src/components/details-categories-field/details-categories-field.tsx index 0d7b8c7bc06..298633b5d73 100644 --- a/packages/js/product-editor/src/components/details-categories-field/details-categories-field.tsx +++ b/packages/js/product-editor/src/components/details-categories-field/details-categories-field.tsx @@ -3,13 +3,14 @@ */ import { __ } from '@wordpress/i18n'; import { useFormContext } from '@woocommerce/components'; -import { Product, ProductCategory } from '@woocommerce/data'; +import { Product } from '@woocommerce/data'; import { createElement } from '@wordpress/element'; /** * Internal dependencies */ import { CategoryField } from './category-field'; +import { ProductCategoryNode } from './use-category-search'; export const DetailsCategoriesField = () => { const { getInputProps } = useFormContext< Product >(); @@ -18,9 +19,7 @@ export const DetailsCategoriesField = () => { [] >( - 'categories' - ) } + { ...getInputProps< ProductCategoryNode[] >( 'categories' ) } /> ); }; diff --git a/packages/js/product-editor/src/components/details-categories-field/test/category-field.test.tsx b/packages/js/product-editor/src/components/details-categories-field/test/category-field.test.tsx index 840026a8e76..d0616672c2e 100644 --- a/packages/js/product-editor/src/components/details-categories-field/test/category-field.test.tsx +++ b/packages/js/product-editor/src/components/details-categories-field/test/category-field.test.tsx @@ -1,94 +1,17 @@ /** * External dependencies */ -import { ReactElement, Component } from 'react'; -import { render, fireEvent } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { Form, FormContextType } from '@woocommerce/components'; -import { Product, ProductCategory } from '@woocommerce/data'; +import { Product } from '@woocommerce/data'; import { createElement } from '@wordpress/element'; /** * Internal dependencies */ import { CategoryField } from '../category-field'; -import { - getCategoriesTreeWithMissingParents, - useCategorySearch, -} from '../use-category-search'; +import { ProductCategoryNode } from '../use-category-search'; -const mockCategoryList = [ - { id: 1, name: 'Clothing', parent: 0 }, - { id: 2, name: 'Hoodies', parent: 1 }, - { id: 3, name: 'Rain gear', parent: 0 }, -] as ProductCategory[]; - -jest.mock( '@woocommerce/components', () => { - const originalModule = jest.requireActual( '@woocommerce/components' ); - - type ChildrenProps = { - items: ProductCategory[]; - isOpen: boolean; - highlightedIndex: number; - getMenuProps: () => Record< string, string >; - getItemProps: () => Record< string, string >; - selectItem: ( item: ProductCategory ) => void; - setInputValue: ( value: string ) => void; - }; - type SelectControlProps = { - children: ( {}: ChildrenProps ) => ReactElement | Component; - items: ProductCategory[]; - label: string; - initialSelectedItems?: ProductCategory[]; - itemToString?: ( item: ProductCategory | null ) => string; - getFilteredItems?: ( - allItems: ProductCategory[], - inputValue: string, - selectedItems: ProductCategory[] - ) => ProductCategory[]; - multiple?: boolean; - onInputChange?: ( value: string | undefined ) => void; - onRemove?: ( item: ProductCategory ) => void; - onSelect?: ( selected: ProductCategory ) => void; - placeholder?: string; - selected: ProductCategory[]; - }; - - return { - ...originalModule, - __experimentalSelectControlMenu: ( { - children, - }: { - children: JSX.Element; - } ) => children, - __experimentalSelectControl: ( { - children, - items, - selected, - }: SelectControlProps ) => { - return ( -
    - [select-control] -
    - { selected.map( ( item ) => ( -
    { item.name }
    - ) ) } -
    -
    - { children( { - items, - isOpen: true, - getMenuProps: () => ( {} ), - selectItem: () => {}, - highlightedIndex: -1, - setInputValue: () => {}, - getItemProps: () => ( {} ), - } ) } -
    -
    - ); - }, - }; -} ); jest.mock( '@woocommerce/tracks', () => ( { recordEvent: jest.fn() } ) ); jest.mock( '../use-category-search', () => { @@ -98,7 +21,7 @@ jest.mock( '../use-category-search', () => { originalModule.getCategoriesTreeWithMissingParents, useCategorySearch: jest.fn().mockReturnValue( { searchCategories: jest.fn(), - getFilteredItems: jest.fn(), + getFilteredItemsForSelectTree: jest.fn().mockReturnValue( [] ), isSearching: false, categoriesSelectList: [], categoryTreeKeyValues: {}, @@ -112,24 +35,25 @@ describe( 'CategoryField', () => { } ); it( 'should render a dropdown select control', () => { - const { queryByText } = render( + const { queryByText, queryByPlaceholderText } = render(
    { ( { getInputProps }: FormContextType< Product > ) => ( [] - >( 'categories' ) } + { ...getInputProps< ProductCategoryNode[] >( + 'categories' + ) } /> ) } ); - expect( queryByText( '[select-control]' ) ).toBeInTheDocument(); + queryByPlaceholderText( 'Search or create category…' )?.focus(); + expect( queryByText( 'Create new' ) ).toBeInTheDocument(); } ); it( 'should pass in the selected categories as select control items', () => { - const { queryByText } = render( + const { queryAllByText, queryByPlaceholderText } = render(
    { [] - >( 'categories' ) } + { ...getInputProps< ProductCategoryNode[] >( + 'categories' + ) } /> ) } ); - expect( queryByText( 'Test' ) ).toBeInTheDocument(); - expect( queryByText( 'Clothing' ) ).toBeInTheDocument(); - } ); - - describe( 'search values', () => { - beforeEach( async () => { - const items = await getCategoriesTreeWithMissingParents( - mockCategoryList, - '' - ); - ( useCategorySearch as jest.Mock ).mockReturnValue( { - searchCategories: jest.fn(), - getFilteredItems: jest.fn(), - isSearching: false, - categoriesSelectList: items[ 0 ], - categoryTreeKeyValues: items[ 2 ], - } ); - } ); - - it( 'should display only the parent categories passed in to the categoriesSelectList', () => { - const { queryByText } = render( -
    - { ( { getInputProps }: FormContextType< Product > ) => ( - [] - >( 'categories' ) } - /> - ) } - - ); - expect( - queryByText( mockCategoryList[ 0 ].name ) - ).toBeInTheDocument(); - const childParent = queryByText( - mockCategoryList[ 1 ].name - )?.parentElement?.closest( - '.woocommerce-category-field-dropdown__item-children' - ); - expect( childParent ).toBeInTheDocument(); - expect( childParent?.className ).not.toMatch( - 'woocommerce-category-field-dropdown__item-open' - ); - expect( - queryByText( mockCategoryList[ 2 ].name ) - ).toBeInTheDocument(); - } ); - - it( 'should show selected categories as selected', () => { - const { getByLabelText } = render( -
    - { ( { getInputProps }: FormContextType< Product > ) => ( - [] - >( 'categories' ) } - /> - ) } - - ); - const rainGearCheckbox = getByLabelText( - mockCategoryList[ 2 ].name - ); - expect( rainGearCheckbox ).toBeChecked(); - const clothingCheckbox = getByLabelText( - mockCategoryList[ 0 ].name - ); - expect( clothingCheckbox ).not.toBeChecked(); - } ); - - it( 'should show selected categories as selected', () => { - const { getByLabelText } = render( -
    - { ( { getInputProps }: FormContextType< Product > ) => ( - [] - >( 'categories' ) } - /> - ) } - - ); - const rainGearCheckbox = getByLabelText( - mockCategoryList[ 2 ].name - ); - expect( rainGearCheckbox ).toBeChecked(); - const clothingCheckbox = getByLabelText( - mockCategoryList[ 0 ].name - ); - expect( clothingCheckbox ).not.toBeChecked(); - } ); - - it( 'should include a toggle icon for parents that contain children', () => { - const { getByLabelText } = render( -
    - { ( { getInputProps }: FormContextType< Product > ) => ( - [] - >( 'categories' ) } - /> - ) } - - ); - const rainGearCheckboxParent = getByLabelText( - mockCategoryList[ 0 ].name - ).parentElement?.closest( - '.woocommerce-category-field-dropdown__item-content' - ); - - expect( - rainGearCheckboxParent?.querySelector( 'svg' ) - ).toBeInTheDocument(); - } ); - - it( 'should allow user to toggle the parents using the svg button', () => { - const { getByLabelText, queryByText } = render( -
    - { ( { getInputProps }: FormContextType< Product > ) => ( - [] - >( 'categories' ) } - /> - ) } - - ); - const rainGearCheckboxParent = getByLabelText( - mockCategoryList[ 0 ].name - ).parentElement?.closest( - '.woocommerce-category-field-dropdown__item-content' - ); - - const toggle = rainGearCheckboxParent?.querySelector( 'svg' ); - if ( toggle ) { - fireEvent.click( toggle ); - } - const childParent = queryByText( - mockCategoryList[ 1 ].name - )?.parentElement?.closest( - '.woocommerce-category-field-dropdown__item-children' - ); - expect( childParent ).toBeInTheDocument(); - expect( childParent?.className ).toMatch( - 'woocommerce-category-field-dropdown__item-open' - ); - } ); + queryByPlaceholderText( 'Search or create category…' )?.focus(); + expect( queryAllByText( 'Test' ) ).toHaveLength( 2 ); + expect( queryAllByText( 'Clothing' ) ).toHaveLength( 2 ); } ); } ); diff --git a/packages/js/product-editor/src/components/details-categories-field/use-category-search.ts b/packages/js/product-editor/src/components/details-categories-field/use-category-search.ts index 26ef004384c..cfd5b14a774 100644 --- a/packages/js/product-editor/src/components/details-categories-field/use-category-search.ts +++ b/packages/js/product-editor/src/components/details-categories-field/use-category-search.ts @@ -9,6 +9,7 @@ import { ProductCategory, } from '@woocommerce/data'; import { escapeRegExp } from 'lodash'; +import { TreeItemType } from '@woocommerce/components'; /** * Internal dependencies @@ -33,6 +34,11 @@ function openParents( } } +export type ProductCategoryNode = Pick< + ProductCategory, + 'id' | 'name' | 'parent' +>; + /** * Sort function for category tree items, sorts by popularity and then alphabetically. */ @@ -256,9 +262,9 @@ export const useCategorySearch = () => { */ const getFilteredItems = useCallback( ( - allItems: Pick< ProductCategory, 'id' | 'name' >[], + allItems: ProductCategoryNode[], inputValue: string, - selectedItems: Pick< ProductCategory, 'id' | 'name' >[] + selectedItems: ProductCategoryNode[] ) => { const searchRegex = new RegExp( escapeRegExp( inputValue ), 'i' ); return allItems.filter( @@ -272,9 +278,33 @@ export const useCategorySearch = () => { [ categoriesAndNewItem ] ); + /** + * this is the same as getFilteredItems but for the SelectTree component, where item id is a string. + * After all the occurrences of getFilteredItems are migrated to use SelectTree, + * this can become the standard version + */ + const getFilteredItemsForSelectTree = useCallback( + ( + allItems: TreeItemType[], + inputValue: string, + selectedItems: TreeItemType[] + ) => { + const searchRegex = new RegExp( escapeRegExp( inputValue ), 'i' ); + return allItems.filter( + ( item ) => + selectedItems.indexOf( item ) < 0 && + ( searchRegex.test( item.label ) || + ( categoryTreeKeyValues[ +item.value ] && + categoryTreeKeyValues[ +item.value ].isOpen ) ) + ); + }, + [ categoriesAndNewItem ] + ); + return { searchCategories, getFilteredItems, + getFilteredItemsForSelectTree, categoriesSelectList: categoriesAndNewItem[ 0 ], categories: categoriesAndNewItem[ 1 ], isSearching, From f7572b436162b033cc73c2ccdabea777387774da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maikel=20David=20P=C3=A9rez=20G=C3=B3mez?= Date: Tue, 4 Apr 2023 11:30:39 -0400 Subject: [PATCH 125/466] Move the Save draft and Preview buttons to the product editor header (#37461) * Add preview button to the product editor header * Publish the product on add or on save * Add save draft button * Save product changes before previewing it * Add notice messages * Fix linter errors * Force the store page to be openned in a new tab after publishing the product * Enable publish button when the status is not publish * Add changelog files * Fix some comment typos * Address comment suggestions * Move preview logic to its own component * Move save draft logic to its own component * Move publish logic to its own component --- .../js/product-editor/changelog/add-37249 | 4 + .../src/components/editor/editor.tsx | 7 +- .../src/components/header/header.tsx | 79 ++-------- .../header/hooks/use-preview/index.ts | 1 + .../header/hooks/use-preview/use-preview.tsx | 140 ++++++++++++++++++ .../header/hooks/use-publish/index.ts | 1 + .../header/hooks/use-publish/use-publish.tsx | 105 +++++++++++++ .../header/hooks/use-save-draft/index.ts | 1 + .../hooks/use-save-draft/use-save-draft.tsx | 109 ++++++++++++++ .../components/header/preview-button/index.ts | 1 + .../header/preview-button/preview-button.tsx | 47 ++++++ .../components/header/publish-button/index.ts | 1 + .../header/publish-button/publish-button.tsx | 72 +++++++++ .../header/save-draft-button/index.ts | 1 + .../save-draft-button/save-draft-button.tsx | 49 ++++++ .../client/stylesheets/shared/_global.scss | 2 +- plugins/woocommerce/changelog/add-37249 | 4 + 17 files changed, 554 insertions(+), 70 deletions(-) create mode 100644 packages/js/product-editor/changelog/add-37249 create mode 100644 packages/js/product-editor/src/components/header/hooks/use-preview/index.ts create mode 100644 packages/js/product-editor/src/components/header/hooks/use-preview/use-preview.tsx create mode 100644 packages/js/product-editor/src/components/header/hooks/use-publish/index.ts create mode 100644 packages/js/product-editor/src/components/header/hooks/use-publish/use-publish.tsx create mode 100644 packages/js/product-editor/src/components/header/hooks/use-save-draft/index.ts create mode 100644 packages/js/product-editor/src/components/header/hooks/use-save-draft/use-save-draft.tsx create mode 100644 packages/js/product-editor/src/components/header/preview-button/index.ts create mode 100644 packages/js/product-editor/src/components/header/preview-button/preview-button.tsx create mode 100644 packages/js/product-editor/src/components/header/publish-button/index.ts create mode 100644 packages/js/product-editor/src/components/header/publish-button/publish-button.tsx create mode 100644 packages/js/product-editor/src/components/header/save-draft-button/index.ts create mode 100644 packages/js/product-editor/src/components/header/save-draft-button/save-draft-button.tsx create mode 100644 plugins/woocommerce/changelog/add-37249 diff --git a/packages/js/product-editor/changelog/add-37249 b/packages/js/product-editor/changelog/add-37249 new file mode 100644 index 00000000000..9bcccfd3224 --- /dev/null +++ b/packages/js/product-editor/changelog/add-37249 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add action buttons to the editor header diff --git a/packages/js/product-editor/src/components/editor/editor.tsx b/packages/js/product-editor/src/components/editor/editor.tsx index 94a4e36e871..9d475b542ee 100644 --- a/packages/js/product-editor/src/components/editor/editor.tsx +++ b/packages/js/product-editor/src/components/editor/editor.tsx @@ -48,12 +48,7 @@ export function Editor( { product, settings }: EditorProps ) { - } + header={
    } content={ <> { - const { isSavingEntityRecord, getEditedEntityRecord } = - select( 'core' ); - const { isPostSavingLocked } = select( 'core/editor' ); - - const product: Product = getEditedEntityRecord( - 'postType', - 'product', - productId - ); - - return { - isProductLocked: isPostSavingLocked(), - isSaving: isSavingEntityRecord( - 'postType', - 'product', - productId - ), - editedProductName: product?.name, - }; - }, - [ productId ] +export function Header( { productName }: HeaderProps ) { + const [ editedProductName ] = useEntityProp< string >( + 'postType', + 'product', + 'name' ); - const isDisabled = isProductLocked || isSaving; - const isCreating = productName === AUTO_DRAFT_NAME; - - const { saveEditedEntityRecord } = useDispatch( 'core' ); - - function handleSave() { - saveEditedEntityRecord< Product >( - 'postType', - 'product', - productId - ).then( ( response ) => { - if ( isCreating ) { - navigateTo( { - url: getNewPath( {}, `/product/${ response.id }` ), - } ); - } - } ); - } - return (
    - + + + + + +
    diff --git a/packages/js/product-editor/src/components/header/hooks/use-preview/index.ts b/packages/js/product-editor/src/components/header/hooks/use-preview/index.ts new file mode 100644 index 00000000000..872825f555c --- /dev/null +++ b/packages/js/product-editor/src/components/header/hooks/use-preview/index.ts @@ -0,0 +1 @@ +export * from './use-preview'; diff --git a/packages/js/product-editor/src/components/header/hooks/use-preview/use-preview.tsx b/packages/js/product-editor/src/components/header/hooks/use-preview/use-preview.tsx new file mode 100644 index 00000000000..a2bbecdc43d --- /dev/null +++ b/packages/js/product-editor/src/components/header/hooks/use-preview/use-preview.tsx @@ -0,0 +1,140 @@ +/** + * External dependencies + */ +import { Product, ProductStatus } from '@woocommerce/data'; +import { Button } from '@wordpress/components'; +import { useEntityProp } from '@wordpress/core-data'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { useRef } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { MouseEvent } from 'react'; + +export function usePreview( { + disabled, + onClick, + onSaveSuccess, + onSaveError, + ...props +}: Omit< Button.AnchorProps, 'aria-disabled' | 'variant' | 'href' > & { + onSaveSuccess?( product: Product ): void; + onSaveError?( error: Error ): void; +} ): Button.AnchorProps { + const anchorRef = useRef< HTMLAnchorElement >(); + + const [ productId ] = useEntityProp< number >( + 'postType', + 'product', + 'id' + ); + const [ productStatus ] = useEntityProp< ProductStatus | 'auto-draft' >( + 'postType', + 'product', + 'status' + ); + const [ permalink ] = useEntityProp< string >( + 'postType', + 'product', + 'permalink' + ); + + const { hasEdits, isDisabled } = useSelect( + ( select ) => { + const { hasEditsForEntityRecord, isSavingEntityRecord } = + select( 'core' ); + const { isPostSavingLocked } = select( 'core/editor' ); + const isSavingLocked = isPostSavingLocked(); + const isSaving = isSavingEntityRecord< boolean >( + 'postType', + 'product', + productId + ); + + return { + isDisabled: isSavingLocked || isSaving, + hasEdits: hasEditsForEntityRecord< boolean >( + 'postType', + 'product', + productId + ), + }; + }, + [ productId ] + ); + + const { editEntityRecord, saveEditedEntityRecord } = useDispatch( 'core' ); + + let previewLink: URL | undefined; + if ( typeof permalink === 'string' ) { + previewLink = new URL( permalink ); + previewLink.searchParams.append( 'preview', 'true' ); + } + + /** + * Overrides the default anchor behaviour when the product has unsaved changes. + * Before redirecting to the preview page all changes are saved and then the + * redirection is performed. + * + * @param event + */ + async function handleClick( event: MouseEvent< HTMLAnchorElement > ) { + if ( onClick ) { + onClick( event ); + } + + // Prevent an infinite recursion call due to the + // `anchorRef.current?.click()` call. + if ( ! hasEdits ) { + return; + } + + // Prevent the default anchor behaviour. + event.preventDefault(); + + try { + // If the product status is `auto-draft` it's not possible to + // reach the preview page, so the status is changed to `draft` + // before redirecting. + if ( productStatus === 'auto-draft' ) { + await editEntityRecord( 'postType', 'product', productId, { + status: 'draft', + } ); + } + + // Persist the product changes before redirecting + const publishedProduct = await saveEditedEntityRecord< Product >( + 'postType', + 'product', + productId + ); + + // Redirect using the default anchor behaviour. This way, the usage + // of `window.open` is avoided which comes with some edge cases. + anchorRef.current?.click(); + + if ( onSaveSuccess ) { + onSaveSuccess( publishedProduct ); + } + } catch ( error ) { + if ( onSaveError ) { + onSaveError( error as Error ); + } + } + } + + return { + 'aria-label': __( 'Preview in new tab', 'woocommerce' ), + children: __( 'Preview', 'woocommerce' ), + target: '_blank', + ...props, + ref( element: HTMLAnchorElement ) { + if ( typeof props.ref === 'function' ) props.ref( element ); + anchorRef.current = element; + }, + 'aria-disabled': disabled || isDisabled, + // Note that the href is always passed for a11y support. So + // the final rendered element is always an anchor. + href: previewLink?.toString(), + variant: 'tertiary', + onClick: handleClick, + }; +} diff --git a/packages/js/product-editor/src/components/header/hooks/use-publish/index.ts b/packages/js/product-editor/src/components/header/hooks/use-publish/index.ts new file mode 100644 index 00000000000..8a01d3e84ae --- /dev/null +++ b/packages/js/product-editor/src/components/header/hooks/use-publish/index.ts @@ -0,0 +1 @@ +export * from './use-publish'; diff --git a/packages/js/product-editor/src/components/header/hooks/use-publish/use-publish.tsx b/packages/js/product-editor/src/components/header/hooks/use-publish/use-publish.tsx new file mode 100644 index 00000000000..8a084e1655c --- /dev/null +++ b/packages/js/product-editor/src/components/header/hooks/use-publish/use-publish.tsx @@ -0,0 +1,105 @@ +/** + * External dependencies + */ +import { Product, ProductStatus } from '@woocommerce/data'; +import { Button } from '@wordpress/components'; +import { useEntityProp } from '@wordpress/core-data'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { MouseEvent } from 'react'; + +export function usePublish( { + disabled, + onClick, + onPublishSuccess, + onPublishError, + ...props +}: Omit< Button.ButtonProps, 'aria-disabled' | 'variant' | 'children' > & { + onPublishSuccess?( product: Product ): void; + onPublishError?( error: Error ): void; +} ): Button.ButtonProps { + const [ productId ] = useEntityProp< number >( + 'postType', + 'product', + 'id' + ); + const [ productStatus ] = useEntityProp< ProductStatus | 'auto-draft' >( + 'postType', + 'product', + 'status' + ); + + const { hasEdits, isDisabled, isBusy } = useSelect( + ( select ) => { + const { hasEditsForEntityRecord, isSavingEntityRecord } = + select( 'core' ); + const { isPostSavingLocked } = select( 'core/editor' ); + const isSavingLocked = isPostSavingLocked(); + const isSaving = isSavingEntityRecord< boolean >( + 'postType', + 'product', + productId + ); + + return { + isDisabled: isSavingLocked || isSaving, + isBusy: isSaving, + hasEdits: hasEditsForEntityRecord< boolean >( + 'postType', + 'product', + productId + ), + }; + }, + [ productId ] + ); + + const isCreating = productStatus === 'auto-draft'; + + const { editEntityRecord, saveEditedEntityRecord } = useDispatch( 'core' ); + + async function handleClick( event: MouseEvent< HTMLButtonElement > ) { + if ( onClick ) { + onClick( event ); + } + + try { + // The publish button click not only change the status of the product + // but also save all the pending changes. So even if the status is + // publish it's possible to save the product too. + if ( productStatus !== 'publish' ) { + await editEntityRecord( 'postType', 'product', productId, { + status: 'publish', + } ); + } + + const publishedProduct = await saveEditedEntityRecord< Product >( + 'postType', + 'product', + productId + ); + + if ( onPublishSuccess ) { + onPublishSuccess( publishedProduct ); + } + } catch ( error ) { + if ( onPublishError ) { + onPublishError( error as Error ); + } + } + } + + return { + children: isCreating + ? __( 'Add', 'woocommerce' ) + : __( 'Save', 'woocommerce' ), + ...props, + 'aria-disabled': + disabled || + isDisabled || + ( productStatus === 'publish' && ! hasEdits ), + isBusy, + variant: 'primary', + onClick: handleClick, + }; +} diff --git a/packages/js/product-editor/src/components/header/hooks/use-save-draft/index.ts b/packages/js/product-editor/src/components/header/hooks/use-save-draft/index.ts new file mode 100644 index 00000000000..679dac40f10 --- /dev/null +++ b/packages/js/product-editor/src/components/header/hooks/use-save-draft/index.ts @@ -0,0 +1 @@ +export * from './use-save-draft'; diff --git a/packages/js/product-editor/src/components/header/hooks/use-save-draft/use-save-draft.tsx b/packages/js/product-editor/src/components/header/hooks/use-save-draft/use-save-draft.tsx new file mode 100644 index 00000000000..fff02bd5451 --- /dev/null +++ b/packages/js/product-editor/src/components/header/hooks/use-save-draft/use-save-draft.tsx @@ -0,0 +1,109 @@ +/** + * External dependencies + */ +import { Product, ProductStatus } from '@woocommerce/data'; +import { Button, Icon } from '@wordpress/components'; +import { useEntityProp } from '@wordpress/core-data'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { __ } from '@wordpress/i18n'; +import { check } from '@wordpress/icons'; +import { createElement, Fragment } from '@wordpress/element'; +import { MouseEvent, ReactNode } from 'react'; + +export function useSaveDraft( { + disabled, + onClick, + onSaveSuccess, + onSaveError, + ...props +}: Omit< Button.ButtonProps, 'aria-disabled' | 'variant' | 'children' > & { + onSaveSuccess?( product: Product ): void; + onSaveError?( error: Error ): void; +} ): Button.ButtonProps { + const [ productId ] = useEntityProp< number >( + 'postType', + 'product', + 'id' + ); + const [ productStatus ] = useEntityProp< ProductStatus | 'auto-draft' >( + 'postType', + 'product', + 'status' + ); + + const { hasEdits, isDisabled } = useSelect( + ( select ) => { + const { hasEditsForEntityRecord, isSavingEntityRecord } = + select( 'core' ); + const { isPostSavingLocked } = select( 'core/editor' ); + const isSavingLocked = isPostSavingLocked(); + const isSaving = isSavingEntityRecord< boolean >( + 'postType', + 'product', + productId + ); + + return { + isDisabled: isSavingLocked || isSaving, + hasEdits: hasEditsForEntityRecord< boolean >( + 'postType', + 'product', + productId + ), + }; + }, + [ productId ] + ); + + const { editEntityRecord, saveEditedEntityRecord } = useDispatch( 'core' ); + + async function handleClick( event: MouseEvent< HTMLButtonElement > ) { + if ( onClick ) { + onClick( event ); + } + + try { + await editEntityRecord( 'postType', 'product', productId, { + status: 'draft', + } ); + const publishedProduct = await saveEditedEntityRecord< Product >( + 'postType', + 'product', + productId + ); + + if ( onSaveSuccess ) { + onSaveSuccess( publishedProduct ); + } + } catch ( error ) { + if ( onSaveError ) { + onSaveError( error as Error ); + } + } + } + + let children: ReactNode; + if ( productStatus === 'publish' ) { + children = __( 'Switch to draft', 'woocommerce' ); + } else if ( hasEdits ) { + children = __( 'Save draft', 'woocommerce' ); + } else { + children = ( + <> + + { __( 'Saved', 'woocommerce' ) } + + ); + } + + return { + children, + ...props, + 'aria-disabled': + disabled || + isDisabled || + ( productStatus !== 'publish' && ! hasEdits ), + variant: 'tertiary', + onClick: handleClick, + }; +} diff --git a/packages/js/product-editor/src/components/header/preview-button/index.ts b/packages/js/product-editor/src/components/header/preview-button/index.ts new file mode 100644 index 00000000000..c3a99f6e222 --- /dev/null +++ b/packages/js/product-editor/src/components/header/preview-button/index.ts @@ -0,0 +1 @@ +export * from './preview-button'; diff --git a/packages/js/product-editor/src/components/header/preview-button/preview-button.tsx b/packages/js/product-editor/src/components/header/preview-button/preview-button.tsx new file mode 100644 index 00000000000..3bdc165ffc3 --- /dev/null +++ b/packages/js/product-editor/src/components/header/preview-button/preview-button.tsx @@ -0,0 +1,47 @@ +/** + * External dependencies + */ +import { Product, ProductStatus } from '@woocommerce/data'; +import { getNewPath, navigateTo } from '@woocommerce/navigation'; +import { Button } from '@wordpress/components'; +import { useEntityProp } from '@wordpress/core-data'; +import { useDispatch } from '@wordpress/data'; +import { createElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { usePreview } from '../hooks/use-preview'; + +export function PreviewButton( { + ...props +}: Omit< + Button.AnchorProps, + 'aria-disabled' | 'variant' | 'href' | 'children' +> ) { + const [ productStatus ] = useEntityProp< ProductStatus | 'auto-draft' >( + 'postType', + 'product', + 'status' + ); + + const { createErrorNotice } = useDispatch( 'core/notices' ); + + const previewButtonProps = usePreview( { + ...props, + onSaveSuccess( savedProduct: Product ) { + if ( productStatus === 'auto-draft' ) { + const url = getNewPath( {}, `/product/${ savedProduct.id }` ); + navigateTo( { url } ); + } + }, + onSaveError() { + createErrorNotice( + __( 'Failed to preview product.', 'woocommerce' ) + ); + }, + } ); + + return
    -

    +

    - + From 54eb2a0c43ce03d05a746916ad0bf911c09232dc Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Wed, 5 Apr 2023 15:13:13 +0530 Subject: [PATCH 134/466] Add sort order for migration script. (#37545) --- plugins/woocommerce/changelog/hpos-add_sort | 4 ++++ .../DataStores/Orders/DataSynchronizer.php | 5 ++++- .../Orders/DataSynchronizerTests.php | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 plugins/woocommerce/changelog/hpos-add_sort diff --git a/plugins/woocommerce/changelog/hpos-add_sort b/plugins/woocommerce/changelog/hpos-add_sort new file mode 100644 index 00000000000..dad318f81a1 --- /dev/null +++ b/plugins/woocommerce/changelog/hpos-add_sort @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Add sort order to migration script for consistency. diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php b/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php index 84e9e9cd956..193009492ec 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php @@ -294,7 +294,8 @@ LEFT JOIN $orders_table orders ON posts.ID = orders.id WHERE posts.post_type IN ($order_post_type_placeholders) AND posts.post_status != 'auto-draft' - AND orders.id IS NULL", + AND orders.id IS NULL +ORDER BY posts.ID ASC", $order_post_types ); // phpcs:enable WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare @@ -305,6 +306,7 @@ SELECT posts.ID FROM $wpdb->posts posts INNER JOIN $orders_table orders ON posts.id=orders.id WHERE posts.post_type = '" . self::PLACEHOLDER_ORDER_POST_TYPE . "' AND orders.status not in ( 'auto-draft' ) +ORDER BY posts.id ASC "; break; case self::ID_TYPE_DIFFERENT_UPDATE_DATE: @@ -317,6 +319,7 @@ JOIN $wpdb->posts posts on posts.ID = orders.id WHERE posts.post_type IN ($order_post_type_placeholders) AND orders.date_updated_gmt $operator posts.post_modified_gmt +ORDER BY orders.id ASC ", $order_post_types ); diff --git a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php index fbe5e011fa0..6d1ac547a35 100644 --- a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php +++ b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php @@ -293,4 +293,22 @@ class DataSynchronizerTests extends WC_Unit_Test_Case { 'Meta data deleted from the CPT datastore should also be deleted from the HPOS datastore.' ); } + + /** + * @testDox Orders for migration are picked by ID sorted. + */ + public function test_migration_sort() { + global $wpdb; + $order1 = wc_get_order( OrderHelper::create_order() ); + $order2 = wc_get_order( OrderHelper::create_order() ); + + // Let's update order1 id to be greater than order2 id. + // phpcs:ignore + $max_id = $wpdb->get_var( "SELECT MAX(id) FROM $wpdb->posts" ); + $wpdb->update( $wpdb->posts, array( 'ID' => $max_id + 1 ), array( 'ID' => $order1->get_id() ) ); + + $orders_to_migrate = $this->sut->get_next_batch_to_process( 2 ); + $this->assertEquals( $order2->get_id(), $orders_to_migrate[0] ); + $this->assertEquals( $max_id + 1, $orders_to_migrate[1] ); + } } From 800ee5d6ddce5fddf24d8fdbc63be6cda218b6a8 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Wed, 5 Apr 2023 18:52:00 +0530 Subject: [PATCH 135/466] Minor fixup for getting order ids in verify db command. --- plugins/woocommerce/changelog/fix-order_count | 4 ++++ .../src/Database/Migrations/CustomOrderTable/CLIRunner.php | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 plugins/woocommerce/changelog/fix-order_count diff --git a/plugins/woocommerce/changelog/fix-order_count b/plugins/woocommerce/changelog/fix-order_count new file mode 100644 index 00000000000..b488dca856f --- /dev/null +++ b/plugins/woocommerce/changelog/fix-order_count @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Minor fixup for getting order ids in verify db command. diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php index 0b54d70cae3..1958d2d9677 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php @@ -325,7 +325,7 @@ class CLIRunner { $order_id_start = (int) $assoc_args['start-from']; $order_id_end = (int) $assoc_args['end-at']; $order_id_end = -1 === $order_id_end ? PHP_INT_MAX : $order_id_end; - $order_count = $this->get_verify_order_count( $order_id_start, $order_id_end ); + $order_count = $this->get_verify_order_count( $order_id_start, $order_id_end, false ); $batch_size = ( (int) $assoc_args['batch-size'] ) === 0 ? 500 : (int) $assoc_args['batch-size']; $progress = WP_CLI\Utils\make_progress_bar( 'Order Data Verification', $order_count / $batch_size ); @@ -372,8 +372,8 @@ class CLIRunner { ) ); - $order_id_start = max( $order_ids ); - $remaining_count = $this->get_verify_order_count( $order_id_start, false ); + $order_id_start = max( $order_ids ) + 1; + $remaining_count = $this->get_verify_order_count( $order_id_start, $order_id_end, false ); if ( $remaining_count === $order_count ) { return WP_CLI::error( __( 'Infinite loop detected, aborting. No errors found.', 'woocommerce' ) ); } From b5e74ae90c3ebf406db04fc65703acfd5bb526ec Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Wed, 5 Apr 2023 19:28:15 +0530 Subject: [PATCH 136/466] Add support for verbose command to display errors as they happen. --- .../Migrations/CustomOrderTable/CLIRunner.php | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php index 1958d2d9677..f1579c7d442 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php @@ -294,6 +294,12 @@ class CLIRunner { * default: -1 * --- * + * [--verbose=] + * : Whether to output errors as they happen in batch, or output them all together at the end. + * --- + * default: false + * --- + * * ## EXAMPLES * * # Verify migrated order data, 500 orders at a time. @@ -315,6 +321,7 @@ class CLIRunner { 'batch-size' => 500, 'start-from' => 0, 'end-at' => -1, + 'verbose' => false, ) ); @@ -327,6 +334,7 @@ class CLIRunner { $order_id_end = -1 === $order_id_end ? PHP_INT_MAX : $order_id_end; $order_count = $this->get_verify_order_count( $order_id_start, $order_id_end, false ); $batch_size = ( (int) $assoc_args['batch-size'] ) === 0 ? 500 : (int) $assoc_args['batch-size']; + $verbose = (bool) $assoc_args['verbose']; $progress = WP_CLI\Utils\make_progress_bar( 'Order Data Verification', $order_count / $batch_size ); @@ -353,13 +361,31 @@ class CLIRunner { ) ); $batch_start_time = microtime( true ); - $failed_ids = $failed_ids + $this->post_to_cot_migrator->verify_migrated_orders( $order_ids ); - $failed_ids = $this->verify_meta_data( $order_ids, $failed_ids ); - $processed += count( $order_ids ); + $failed_ids_in_current_batch = $this->post_to_cot_migrator->verify_migrated_orders( $order_ids ); + $failed_ids_in_current_batch = $this->verify_meta_data( $order_ids, $failed_ids_in_current_batch ); + $failed_ids = $failed_ids + $failed_ids_in_current_batch; + $processed += count( $order_ids ); $batch_total_time = microtime( true ) - $batch_start_time; $batch_count ++; $total_time += $batch_total_time; + if ( $verbose && count( $failed_ids_in_current_batch ) > 0 ) { + $errors = print_r( $failed_ids_in_current_batch, true ); + WP_CLI::warning( + sprintf( + /* Translators: %1$d is number of errors and %2$s is the formatted array of order IDs. */ + _n( + '%1$d error found: %2$s. Please review the error above.', + '%1$d errors found: %2$s. Please review the errors above.', + count( $failed_ids_in_current_batch ), + 'woocommerce' + ), + count( $failed_ids_in_current_batch ), + $errors + ) + ); + } + $progress->tick(); WP_CLI::debug( From b7965223850a78de17568f969cd6d4851f3600ff Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Wed, 5 Apr 2023 19:38:01 +0530 Subject: [PATCH 137/466] Applied coding standards. --- .../Database/Migrations/CustomOrderTable/CLIRunner.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php index f1579c7d442..65a3341ebd4 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php @@ -76,7 +76,7 @@ class CLIRunner { sprintf( // translators: %s - link to testing instructions webpage. __( 'Custom order table usage is not enabled. If you are testing, you can enable it by following the testing instructions in %s', 'woocommerce' ), - 'https://developer.woocommerce.com/' // TODO: Change the link when testing instructin page is live. + 'https://github.com/woocommerce/woocommerce/wiki/High-Performance-Order-Storage-Upgrade-Recipe-Book' ) ); } @@ -352,7 +352,7 @@ class CLIRunner { ) ); - $order_ids = $wpdb->get_col( + $order_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = 'shop_order' AND ID >= %d AND ID <= %d ORDER BY ID ASC LIMIT %d", $order_id_start, @@ -360,12 +360,12 @@ class CLIRunner { $batch_size ) ); - $batch_start_time = microtime( true ); + $batch_start_time = microtime( true ); $failed_ids_in_current_batch = $this->post_to_cot_migrator->verify_migrated_orders( $order_ids ); $failed_ids_in_current_batch = $this->verify_meta_data( $order_ids, $failed_ids_in_current_batch ); $failed_ids = $failed_ids + $failed_ids_in_current_batch; - $processed += count( $order_ids ); - $batch_total_time = microtime( true ) - $batch_start_time; + $processed += count( $order_ids ); + $batch_total_time = microtime( true ) - $batch_start_time; $batch_count ++; $total_time += $batch_total_time; From 85c7bb64269307fbe9e690995bfbd7773b57e798 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Wed, 5 Apr 2023 20:44:27 +0530 Subject: [PATCH 138/466] Apply coding standards. --- .../src/Database/Migrations/CustomOrderTable/CLIRunner.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php index 65a3341ebd4..4393ffc9b26 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php @@ -370,6 +370,7 @@ class CLIRunner { $total_time += $batch_total_time; if ( $verbose && count( $failed_ids_in_current_batch ) > 0 ) { + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- This is a CLI command and debugging code is intended. $errors = print_r( $failed_ids_in_current_batch, true ); WP_CLI::warning( sprintf( From bb22fd586489d97ecd65e0bc4d161376618152f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maikel=20David=20P=C3=A9rez=20G=C3=B3mez?= Date: Wed, 5 Apr 2023 12:55:22 -0400 Subject: [PATCH 139/466] Prevent click event when buttons are aria-disabled (#37577) * Prevent click event when buttons are aria-disabled * Add changelog file --- packages/js/product-editor/changelog/fix-37249 | 4 ++++ .../header/hooks/use-preview/use-preview.tsx | 8 +++++++- .../header/hooks/use-publish/use-publish.tsx | 11 +++++++---- .../header/hooks/use-save-draft/use-save-draft.tsx | 12 ++++++++---- 4 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 packages/js/product-editor/changelog/fix-37249 diff --git a/packages/js/product-editor/changelog/fix-37249 b/packages/js/product-editor/changelog/fix-37249 new file mode 100644 index 00000000000..16fafe1d487 --- /dev/null +++ b/packages/js/product-editor/changelog/fix-37249 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Prevent click event when the element is aria-disabled" diff --git a/packages/js/product-editor/src/components/header/hooks/use-preview/use-preview.tsx b/packages/js/product-editor/src/components/header/hooks/use-preview/use-preview.tsx index a2bbecdc43d..41fe6371316 100644 --- a/packages/js/product-editor/src/components/header/hooks/use-preview/use-preview.tsx +++ b/packages/js/product-editor/src/components/header/hooks/use-preview/use-preview.tsx @@ -61,6 +61,8 @@ export function usePreview( { [ productId ] ); + const ariaDisabled = disabled || isDisabled; + const { editEntityRecord, saveEditedEntityRecord } = useDispatch( 'core' ); let previewLink: URL | undefined; @@ -77,6 +79,10 @@ export function usePreview( { * @param event */ async function handleClick( event: MouseEvent< HTMLAnchorElement > ) { + if ( ariaDisabled ) { + return event.preventDefault(); + } + if ( onClick ) { onClick( event ); } @@ -130,7 +136,7 @@ export function usePreview( { if ( typeof props.ref === 'function' ) props.ref( element ); anchorRef.current = element; }, - 'aria-disabled': disabled || isDisabled, + 'aria-disabled': ariaDisabled, // Note that the href is always passed for a11y support. So // the final rendered element is always an anchor. href: previewLink?.toString(), diff --git a/packages/js/product-editor/src/components/header/hooks/use-publish/use-publish.tsx b/packages/js/product-editor/src/components/header/hooks/use-publish/use-publish.tsx index 8a084e1655c..6b1b2b47979 100644 --- a/packages/js/product-editor/src/components/header/hooks/use-publish/use-publish.tsx +++ b/packages/js/product-editor/src/components/header/hooks/use-publish/use-publish.tsx @@ -55,10 +55,16 @@ export function usePublish( { ); const isCreating = productStatus === 'auto-draft'; + const ariaDisabled = + disabled || isDisabled || ( productStatus === 'publish' && ! hasEdits ); const { editEntityRecord, saveEditedEntityRecord } = useDispatch( 'core' ); async function handleClick( event: MouseEvent< HTMLButtonElement > ) { + if ( ariaDisabled ) { + return event.preventDefault(); + } + if ( onClick ) { onClick( event ); } @@ -94,10 +100,7 @@ export function usePublish( { ? __( 'Add', 'woocommerce' ) : __( 'Save', 'woocommerce' ), ...props, - 'aria-disabled': - disabled || - isDisabled || - ( productStatus === 'publish' && ! hasEdits ), + 'aria-disabled': ariaDisabled, isBusy, variant: 'primary', onClick: handleClick, diff --git a/packages/js/product-editor/src/components/header/hooks/use-save-draft/use-save-draft.tsx b/packages/js/product-editor/src/components/header/hooks/use-save-draft/use-save-draft.tsx index fff02bd5451..d8df2f5a8b0 100644 --- a/packages/js/product-editor/src/components/header/hooks/use-save-draft/use-save-draft.tsx +++ b/packages/js/product-editor/src/components/header/hooks/use-save-draft/use-save-draft.tsx @@ -55,9 +55,16 @@ export function useSaveDraft( { [ productId ] ); + const ariaDisabled = + disabled || isDisabled || ( productStatus !== 'publish' && ! hasEdits ); + const { editEntityRecord, saveEditedEntityRecord } = useDispatch( 'core' ); async function handleClick( event: MouseEvent< HTMLButtonElement > ) { + if ( ariaDisabled ) { + return event.preventDefault(); + } + if ( onClick ) { onClick( event ); } @@ -99,10 +106,7 @@ export function useSaveDraft( { return { children, ...props, - 'aria-disabled': - disabled || - isDisabled || - ( productStatus !== 'publish' && ! hasEdits ), + 'aria-disabled': ariaDisabled, variant: 'tertiary', onClick: handleClick, }; From a5fe4232c797c87291a18ac7d7a90a922968496e Mon Sep 17 00:00:00 2001 From: Fernando Marichal Date: Wed, 5 Apr 2023 13:56:42 -0300 Subject: [PATCH 140/466] Change variations dropdown visibility (#37558) * Add default action with the name `Bulk actions` * Attach event when dropdown changes * Add changelog * Remove `Go` button * Fix e2e tests * Fix styles --------- Co-authored-by: Fernando Marichal --- ...add-37148_change_variations_dropdown_visibility | 4 ++++ plugins/woocommerce/client/legacy/css/admin.scss | 7 ++++++- .../js/admin/meta-boxes-product-variation.js | 14 ++++++-------- .../views/html-product-data-variations.php | 4 ++-- .../tests/merchant/create-variable-product.spec.js | 2 -- 5 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 plugins/woocommerce/changelog/add-37148_change_variations_dropdown_visibility diff --git a/plugins/woocommerce/changelog/add-37148_change_variations_dropdown_visibility b/plugins/woocommerce/changelog/add-37148_change_variations_dropdown_visibility new file mode 100644 index 00000000000..5209f44c4a5 --- /dev/null +++ b/plugins/woocommerce/changelog/add-37148_change_variations_dropdown_visibility @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Change variations dropdown menu visibility diff --git a/plugins/woocommerce/client/legacy/css/admin.scss b/plugins/woocommerce/client/legacy/css/admin.scss index db87b9c7344..ad9a3e3d1cf 100644 --- a/plugins/woocommerce/client/legacy/css/admin.scss +++ b/plugins/woocommerce/client/legacy/css/admin.scss @@ -1018,8 +1018,13 @@ $default-line-height: 18px; max-width: 100%; } + .variation_actions { + max-width: 131px; + } + .toolbar-top { - .button { + .button, + .select { margin: 1px; } } diff --git a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js index 4d43ad94999..f75438fb80c 100644 --- a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js +++ b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js @@ -655,8 +655,8 @@ jQuery( function ( $ ) { } ); $( '.wc-metaboxes-wrapper' ).on( - 'click', - 'a.do_variation_action', + 'change', + '#field_to_edit', this.do_variation_action ); @@ -1195,7 +1195,7 @@ jQuery( function ( $ ) { * Actions */ do_variation_action: function () { - var do_variation_action = $( 'select.variation_actions' ).val(), + var do_variation_action = $( this ).val(), data = {}, changes = 0, value; @@ -1344,11 +1344,9 @@ jQuery( function ( $ ) { if ( parseInt( wrapper.attr( 'data-total' ) ) > 0 ) { $( '.add-variation-container' ).addClass( 'hidden' ); $( '#field_to_edit' ).removeClass( 'hidden' ); - $( 'a.do_variation_action' ).removeClass( 'hidden' ); } else { $( '.add-variation-container' ).removeClass( 'hidden' ); $( '#field_to_edit' ).addClass( 'hidden' ); - $( 'a.do_variation_action' ).addClass( 'hidden' ); } }, @@ -1451,7 +1449,7 @@ jQuery( function ( $ ) { if ( page_nav.is( ':hidden' ) ) { $( 'option, optgroup', '.variation_actions' ).show(); - $( '.variation_actions' ).val( 'delete_all' ); + $( '.variation_actions' ).val( 'bulk_actions' ); $( '#variable_product_options' ).find( '.toolbar' ).show(); page_nav.show(); $( '.pagination-links', page_nav ).hide(); @@ -1498,13 +1496,13 @@ jQuery( function ( $ ) { toolbar.not( '.toolbar-top, .toolbar-buttons' ).hide(); page_nav.hide(); $( 'option, optgroup', variation_action ).hide(); - $( '.variation_actions' ).val( 'delete_all' ); + $( '.variation_actions' ).val( 'bulk_actions' ); $( 'option[data-global="true"]', variation_action ).show(); } else { toolbar.show(); page_nav.show(); $( 'option, optgroup', variation_action ).show(); - $( '.variation_actions' ).val( 'delete_all' ); + $( '.variation_actions' ).val( 'bulk_actions' ); // Show/hide links if ( 1 === total_pages ) { diff --git a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php index 06507a54061..7c37b7af4a1 100644 --- a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php +++ b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php @@ -97,7 +97,8 @@ $arrow_img_url = WC_ADMIN_IMAGES_FOLDER_URL . '/product_data/no-variati

    - + @@ -135,7 +136,6 @@ $arrow_img_url = WC_ADMIN_IMAGES_FOLDER_URL . '/product_data/no-variati -
    diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-variable-product.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-variable-product.spec.js index 04c139e1d4b..32fae78413c 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-variable-product.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/create-variable-product.spec.js @@ -179,7 +179,6 @@ test.describe( 'Add New Variable Product Page', () => { await page.selectOption( '#field_to_edit', 'toggle_downloadable', { force: true, } ); - await page.click( 'a.do_variation_action' ); await page.click( '#variable_product_options .toolbar-top a.expand_all' ); @@ -196,7 +195,6 @@ test.describe( 'Add New Variable Product Page', () => { await page.click( 'a[href="#variable_product_options"]' ); await page.waitForLoadState( 'networkidle' ); await page.selectOption( '#field_to_edit', 'delete_all' ); - await page.click( 'a.do_variation_action' ); await page.waitForSelector( '.woocommerce_variation', { state: 'detached', } ); From 7734027b4a6e1066b220f1f3c0a5b08b9e8ac29c Mon Sep 17 00:00:00 2001 From: Joshua T Flowers Date: Wed, 5 Apr 2023 10:13:05 -0700 Subject: [PATCH 141/466] Add product radio block and tax class to product blocks editor (#37529) * Add product radio block * Wrap tax class radio in collapsible block * Add link to description and sanitize HTML * Add changelog entry * Fix up php lint errors * Add changelog entry * Fix linting errors * Remove unused import --- .../js/product-editor/changelog/add-37395 | 4 ++ .../components/collapsible-block/block.json | 3 +- .../src/components/editor/init-blocks.ts | 2 + .../src/components/radio/block.json | 39 ++++++++++++++++ .../src/components/radio/edit.tsx | 46 +++++++++++++++++++ .../src/components/radio/editor.scss | 27 +++++++++++ .../src/components/radio/index.ts | 17 +++++++ packages/js/product-editor/src/style.scss | 1 + plugins/woocommerce/changelog/add-37395 | 4 ++ .../includes/class-wc-post-types.php | 37 +++++++++++++++ 10 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 packages/js/product-editor/changelog/add-37395 create mode 100644 packages/js/product-editor/src/components/radio/block.json create mode 100644 packages/js/product-editor/src/components/radio/edit.tsx create mode 100644 packages/js/product-editor/src/components/radio/editor.scss create mode 100644 packages/js/product-editor/src/components/radio/index.ts create mode 100644 plugins/woocommerce/changelog/add-37395 diff --git a/packages/js/product-editor/changelog/add-37395 b/packages/js/product-editor/changelog/add-37395 new file mode 100644 index 00000000000..737428f453f --- /dev/null +++ b/packages/js/product-editor/changelog/add-37395 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add product radio block and tax class to product blocks editor diff --git a/packages/js/product-editor/src/components/collapsible-block/block.json b/packages/js/product-editor/src/components/collapsible-block/block.json index d0a6e6deac5..07e301c32fc 100644 --- a/packages/js/product-editor/src/components/collapsible-block/block.json +++ b/packages/js/product-editor/src/components/collapsible-block/block.json @@ -8,7 +8,8 @@ "textdomain": "default", "attributes": { "toggleText": { - "type": "string" + "type": "string", + "__experimentalRole": "content" }, "initialCollapsed": { "type": "boolean" diff --git a/packages/js/product-editor/src/components/editor/init-blocks.ts b/packages/js/product-editor/src/components/editor/init-blocks.ts index 7a49334fb22..7555c6a2a1f 100644 --- a/packages/js/product-editor/src/components/editor/init-blocks.ts +++ b/packages/js/product-editor/src/components/editor/init-blocks.ts @@ -14,6 +14,7 @@ import { */ import { init as initImages } from '../images'; import { init as initName } from '../details-name-block'; +import { init as initRadio } from '../radio'; import { init as initSummary } from '../details-summary-block'; import { init as initSection } from '../section'; import { init as initTab } from '../tab'; @@ -31,6 +32,7 @@ export const initBlocks = () => { initImages(); initName(); + initRadio(); initSummary(); initSection(); initTab(); diff --git a/packages/js/product-editor/src/components/radio/block.json b/packages/js/product-editor/src/components/radio/block.json new file mode 100644 index 00000000000..33dec4fcf11 --- /dev/null +++ b/packages/js/product-editor/src/components/radio/block.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "woocommerce/product-radio", + "title": "Product radio control", + "category": "woocommerce", + "description": "The product radio.", + "keywords": [ "products", "radio", "input" ], + "textdomain": "default", + "attributes": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "property": { + "type": "string" + }, + "options": { + "type": "array", + "items": { + "type": "object" + }, + "default": [], + "__experimentalRole": "content" + } + }, + "supports": { + "align": false, + "html": false, + "multiple": true, + "reusable": false, + "inserter": false, + "lock": false, + "__experimentalToolbar": false + }, + "editorStyle": "file:./editor.css" +} diff --git a/packages/js/product-editor/src/components/radio/edit.tsx b/packages/js/product-editor/src/components/radio/edit.tsx new file mode 100644 index 00000000000..77644752595 --- /dev/null +++ b/packages/js/product-editor/src/components/radio/edit.tsx @@ -0,0 +1,46 @@ +/** + * External dependencies + */ +import { createElement, Fragment } from '@wordpress/element'; +import type { BlockAttributes } from '@wordpress/blocks'; +import { RadioControl } from '@wordpress/components'; +import { useBlockProps } from '@wordpress/block-editor'; +import { useEntityProp } from '@wordpress/core-data'; + +/** + * Internal dependencies + */ +import { sanitizeHTML } from '../../utils/sanitize-html'; + +export function Edit( { attributes }: { attributes: BlockAttributes } ) { + const blockProps = useBlockProps(); + const { description, options, property, title } = attributes; + const [ value, setValue ] = useEntityProp< string >( + 'postType', + 'product', + property + ); + + return ( +
    + + + { title } + + + + } + selected={ value } + options={ options } + onChange={ ( selected ) => setValue( selected || '' ) } + /> +
    + ); +} diff --git a/packages/js/product-editor/src/components/radio/editor.scss b/packages/js/product-editor/src/components/radio/editor.scss new file mode 100644 index 00000000000..8b1729d3d81 --- /dev/null +++ b/packages/js/product-editor/src/components/radio/editor.scss @@ -0,0 +1,27 @@ +.wp-block-woocommerce-product-radio { + .components-base-control__label { + text-transform: none; + font-weight: 400; + > span { + display: block; + line-height: 1.5; + } + margin-bottom: $gap-large; + } + + &__title { + font-size: 16px; + font-weight: 600; + color: #1e1e1e; + margin-bottom: $gap-smaller; + } + + &__description { + font-size: 13px; + color: #1e1e1e; + } + + .components-base-control__field > .components-v-stack { + gap: $gap; + } +} \ No newline at end of file diff --git a/packages/js/product-editor/src/components/radio/index.ts b/packages/js/product-editor/src/components/radio/index.ts new file mode 100644 index 00000000000..15144b2b28d --- /dev/null +++ b/packages/js/product-editor/src/components/radio/index.ts @@ -0,0 +1,17 @@ +/** + * Internal dependencies + */ +import initBlock from '../../utils/init-block'; +import metadata from './block.json'; +import { Edit } from './edit'; + +const { name } = metadata; + +export { metadata, name }; + +export const settings = { + example: {}, + edit: Edit, +}; + +export const init = () => initBlock( { name, metadata, settings } ); diff --git a/packages/js/product-editor/src/style.scss b/packages/js/product-editor/src/style.scss index fdba01eac4b..0287d7c8690 100644 --- a/packages/js/product-editor/src/style.scss +++ b/packages/js/product-editor/src/style.scss @@ -6,6 +6,7 @@ @import 'components/header/style.scss'; @import 'components/images/editor.scss'; @import 'components/block-editor/style.scss'; +@import 'components/radio/editor.scss'; @import 'components/section/editor.scss'; @import 'components/tab/editor.scss'; @import 'components/tabs/style.scss'; diff --git a/plugins/woocommerce/changelog/add-37395 b/plugins/woocommerce/changelog/add-37395 new file mode 100644 index 00000000000..0865cfe9c7d --- /dev/null +++ b/plugins/woocommerce/changelog/add-37395 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add tax class to product editor template diff --git a/plugins/woocommerce/includes/class-wc-post-types.php b/plugins/woocommerce/includes/class-wc-post-types.php index 77114474dd5..e42dab1c988 100644 --- a/plugins/woocommerce/includes/class-wc-post-types.php +++ b/plugins/woocommerce/includes/class-wc-post-types.php @@ -513,6 +513,43 @@ class WC_Post_Types { ), ), ), + array( + 'woocommerce/collapsible', + array( + 'toggleText' => __( 'Advanced', 'woocommerce' ), + 'initialCollapsed' => true, + 'persistRender' => true, + ), + array( + array( + 'woocommerce/product-radio', + array( + 'title' => __( 'Tax class', 'woocommerce' ), + 'description' => sprintf( + /* translators: %1$s: Learn more link opening tag. %2$s: Learn more link closing tag.*/ + __( 'Apply a tax rate if this product qualifies for tax reduction or exemption. %1$sLearn more%2$s.', 'woocommerce' ), + '', + '' + ), + 'property' => 'tax_class', + 'options' => array( + array( + 'label' => __( 'Standard', 'woocommerce' ), + 'value' => '', + ), + array( + 'label' => __( 'Reduced rate', 'woocommerce' ), + 'value' => 'reduced-rate', + ), + array( + 'label' => __( 'Zero rate', 'woocommerce' ), + 'value' => 'zero-rate', + ), + ), + ), + ), + ), + ), ), ), ), From 277f4aab12ee5baeb4231f541bd231b93c1b7b45 Mon Sep 17 00:00:00 2001 From: Nathan Silveira Date: Wed, 5 Apr 2023 16:22:59 -0300 Subject: [PATCH 142/466] Show tooltip in Save attributes button instead of using title attribute (#37345) * Modify tipTip to allow showing dynamic content * Show tooltip when button is disabled * Add tipTip and disabled logic to create variations button * Add header comment in tipTip plugin --- .../product-tracking/shared.ts | 9 ++++- .../changelog/update-save-attributes-tooltip | 4 ++ .../js/admin/meta-boxes-product-variation.js | 6 ++- .../legacy/js/admin/meta-boxes-product.js | 6 ++- .../client/legacy/js/admin/meta-boxes.js | 39 ++++++++++++++----- .../legacy/js/jquery-tiptip/jquery.tipTip.js | 13 +++++-- .../views/html-product-data-attributes.php | 2 +- .../views/html-product-data-variations.php | 2 +- 8 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 plugins/woocommerce/changelog/update-save-attributes-tooltip diff --git a/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/shared.ts b/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/shared.ts index 1e40d57ff87..38712440139 100644 --- a/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/shared.ts +++ b/plugins/woocommerce-admin/client/wp-admin-scripts/product-tracking/shared.ts @@ -504,7 +504,14 @@ export const initProductScreenTracks = () => { document .querySelector( '.save_attributes' ) - ?.addEventListener( 'click', () => { + ?.addEventListener( 'click', ( event ) => { + if ( + event.target instanceof Element && + event.target.classList.contains( 'disabled' ) + ) { + // skip in case the button is disabled + return; + } const newAttributesCount = document.querySelectorAll( '.woocommerce_attribute' ).length; diff --git a/plugins/woocommerce/changelog/update-save-attributes-tooltip b/plugins/woocommerce/changelog/update-save-attributes-tooltip new file mode 100644 index 00000000000..7518ccd9ba3 --- /dev/null +++ b/plugins/woocommerce/changelog/update-save-attributes-tooltip @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Show tooltip in Save attributes button instead of using title attribute diff --git a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js index f75438fb80c..7f865a90066 100644 --- a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js +++ b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product-variation.js @@ -56,7 +56,11 @@ jQuery( function ( $ ) { ); }, - create_variations: function () { + create_variations: function ( event ) { + if ( $( this ).hasClass( 'disabled' ) ) { + event.preventDefault(); + return; + } var new_attribute_data = $( '.woocommerce_variation_new_attribute_data' ); diff --git a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product.js b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product.js index 5bc65fd8058..859901b27fa 100644 --- a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product.js +++ b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product.js @@ -750,7 +750,11 @@ jQuery( function ( $ ) { ); // Save attributes and update variations. - $( '.save_attributes' ).on( 'click', function () { + $( '.save_attributes' ).on( 'click', function ( event ) { + if ( $( this ).hasClass( 'disabled' ) ) { + event.preventDefault(); + return; + } $( '.product_attributes' ).block( { message: null, overlayCSS: { diff --git a/plugins/woocommerce/client/legacy/js/admin/meta-boxes.js b/plugins/woocommerce/client/legacy/js/admin/meta-boxes.js index bffcde118e8..f773e673ffa 100644 --- a/plugins/woocommerce/client/legacy/js/admin/meta-boxes.js +++ b/plugins/woocommerce/client/legacy/js/admin/meta-boxes.js @@ -50,17 +50,14 @@ jQuery( function ( $ ) { attributes_and_variations_data ) ) { - if ( ! $save_button.is( ':disabled' ) ) { - $save_button.attr( 'disabled', 'disabled' ); - $save_button.attr( - 'title', - woocommerce_admin_meta_boxes.i18n_save_attribute_variation_tip - ); + if ( ! $save_button.hasClass( 'disabled' ) ) { + $save_button.addClass( 'disabled' ); + $save_button.attr( 'aria-disabled', true ); } - return; + } else { + $save_button.removeClass( 'disabled' ); + $save_button.removeAttr( 'aria-disabled' ); } - $save_button.removeAttr( 'disabled' ); - $save_button.removeAttr( 'title' ); }; // Run tipTip @@ -79,6 +76,30 @@ jQuery( function ( $ ) { runTipTip(); + $( '.save_attributes' ).tipTip( { + content: function () { + return $( '.save_attributes' ).hasClass( 'disabled' ) + ? woocommerce_admin_meta_boxes.i18n_save_attribute_variation_tip + : ''; + }, + fadeIn: 50, + fadeOut: 50, + delay: 200, + keepAlive: true, + } ); + + $( '.create-variations' ).tipTip( { + content: function () { + return $( '.create-variations' ).hasClass( 'disabled' ) + ? woocommerce_admin_meta_boxes.i18n_save_attribute_variation_tip + : ''; + }, + fadeIn: 50, + fadeOut: 50, + delay: 200, + keepAlive: true, + } ); + $( '.wc-metaboxes-wrapper' ).on( 'click', '.wc-metabox > h3', function () { var metabox = $( this ).parent( '.wc-metabox' ); diff --git a/plugins/woocommerce/client/legacy/js/jquery-tiptip/jquery.tipTip.js b/plugins/woocommerce/client/legacy/js/jquery-tiptip/jquery.tipTip.js index 6672f09fc07..8a2d1acc582 100644 --- a/plugins/woocommerce/client/legacy/js/jquery-tiptip/jquery.tipTip.js +++ b/plugins/woocommerce/client/legacy/js/jquery-tiptip/jquery.tipTip.js @@ -4,7 +4,10 @@ * www.drewwilson.com * code.drewwilson.com/entry/tiptip-jquery-plugin * - * Version 1.3 - Updated: Mar. 23, 2010 + * Version 1.3.1 - Updated: Mar. 30, 2023 + * + * This is a custom version of TipTip. This file has been locally modified for specific requirements. + * Since the original version is no longer maintained, the changes were not submitted back to the original author. * * This Plug-In will create a custom tooltip to replace the default * browser tooltip. It is extremely lightweight and very smart in @@ -31,7 +34,7 @@ fadeIn: 200, fadeOut: 200, attribute: "title", - content: false, // HTML or String to fill TipTIp with + content: false, // HTML or String or callback to fill TipTIp with enter: function(){}, exit: function(){} }; @@ -98,8 +101,12 @@ } function active_tiptip(){ + var content = typeof opts.content === 'function' ? opts.content() : org_title; + if (!content) { + return; + } opts.enter.call(this); - tiptip_content.html(org_title); + tiptip_content.html(content); tiptip_holder.hide().css("margin","0"); tiptip_holder.removeAttr('class'); tiptip_arrow.removeAttr("style"); diff --git a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-attributes.php b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-attributes.php index 1858a0c3b45..a757f0414e8 100644 --- a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-attributes.php +++ b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-attributes.php @@ -108,7 +108,7 @@ $icon_url = WC_ADMIN_IMAGES_FOLDER_URL . '/icons/global-a / - +
    diff --git a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php index 7c37b7af4a1..0cf51a2aaaf 100644 --- a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php +++ b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-data-variations.php @@ -53,7 +53,7 @@ $arrow_img_url = WC_ADMIN_IMAGES_FOLDER_URL . '/product_data/no-variati require __DIR__ . '/html-product-attribute-inner.php'; ?>
    - +
    From 07b84051b2b076d3af69be999e5ba1374aff5fa9 Mon Sep 17 00:00:00 2001 From: Joel Thiessen <444632+joelclimbsthings@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:23:58 -0700 Subject: [PATCH 143/466] Adding charge sales tax field to product block editor template (#37582) --- .../changelog/add-charge-sales-tax-37396 | 4 ++++ .../includes/class-wc-post-types.php | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 plugins/woocommerce/changelog/add-charge-sales-tax-37396 diff --git a/plugins/woocommerce/changelog/add-charge-sales-tax-37396 b/plugins/woocommerce/changelog/add-charge-sales-tax-37396 new file mode 100644 index 00000000000..55779d97a07 --- /dev/null +++ b/plugins/woocommerce/changelog/add-charge-sales-tax-37396 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Adding charge sales tax field to product block editor template. diff --git a/plugins/woocommerce/includes/class-wc-post-types.php b/plugins/woocommerce/includes/class-wc-post-types.php index e42dab1c988..d2a8a6083e9 100644 --- a/plugins/woocommerce/includes/class-wc-post-types.php +++ b/plugins/woocommerce/includes/class-wc-post-types.php @@ -513,6 +513,27 @@ class WC_Post_Types { ), ), ), + array( + 'woocommerce/product-radio', + array( + 'title' => __( 'Charge sales tax on', 'woocommerce' ), + 'property' => 'tax_status', + 'options' => array( + array( + 'label' => __( 'Product and shipping', 'woocommerce' ), + 'value' => 'taxable', + ), + array( + 'label' => __( 'Only shipping', 'woocommerce' ), + 'value' => 'shipping', + ), + array( + 'label' => __( "Don't charge tax", 'woocommerce' ), + 'value' => 'none', + ), + ), + ), + ), array( 'woocommerce/collapsible', array( From 33352895bc9aa9cfff222b13df6322b580e8cb16 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 6 Apr 2023 01:56:28 +0200 Subject: [PATCH 144/466] Delete changelog files based on PR 37569 (#37581) Delete changelog files for 37569 Co-authored-by: WooCommerce Bot --- plugins/woocommerce/changelog/fix-order-cache-order-type | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 plugins/woocommerce/changelog/fix-order-cache-order-type diff --git a/plugins/woocommerce/changelog/fix-order-cache-order-type b/plugins/woocommerce/changelog/fix-order-cache-order-type deleted file mode 100644 index 5e58de01771..00000000000 --- a/plugins/woocommerce/changelog/fix-order-cache-order-type +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Do not attempt to cache order during order creation (HPOS). From 6eca1ed56124ffccfb084cb3f28243ddff4d43f2 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Thu, 6 Apr 2023 15:14:06 +0530 Subject: [PATCH 145/466] Prevent errors from being logged twice when verbose is passed. --- .../src/Database/Migrations/CustomOrderTable/CLIRunner.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php index 4393ffc9b26..2a210e453a9 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php @@ -294,7 +294,7 @@ class CLIRunner { * default: -1 * --- * - * [--verbose=] + * [--verbose] * : Whether to output errors as they happen in batch, or output them all together at the end. * --- * default: false @@ -425,6 +425,9 @@ class CLIRunner { ) ); } else { + if ( $verbose ) { + return; + } $errors = print_r( $failed_ids, true ); return WP_CLI::error( From dbb4bd5e27778604c911c322a4f29f11922a3e4e Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Thu, 6 Apr 2023 15:23:19 +0530 Subject: [PATCH 146/466] Dont store order failures when verbose is enabled to save memory. --- .../Database/Migrations/CustomOrderTable/CLIRunner.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php index 2a210e453a9..a25238b7c61 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/CLIRunner.php @@ -363,7 +363,7 @@ class CLIRunner { $batch_start_time = microtime( true ); $failed_ids_in_current_batch = $this->post_to_cot_migrator->verify_migrated_orders( $order_ids ); $failed_ids_in_current_batch = $this->verify_meta_data( $order_ids, $failed_ids_in_current_batch ); - $failed_ids = $failed_ids + $failed_ids_in_current_batch; + $failed_ids = $verbose ? array() : $failed_ids + $failed_ids_in_current_batch; $processed += count( $order_ids ); $batch_total_time = microtime( true ) - $batch_start_time; $batch_count ++; @@ -410,6 +410,10 @@ class CLIRunner { $progress->finish(); WP_CLI::log( __( 'Verification completed.', 'woocommerce' ) ); + if ( $verbose ) { + return; + } + if ( 0 === count( $failed_ids ) ) { return WP_CLI::success( sprintf( @@ -425,9 +429,6 @@ class CLIRunner { ) ); } else { - if ( $verbose ) { - return; - } $errors = print_r( $failed_ids, true ); return WP_CLI::error( From ebb43378a1198fbd9a380fbb0b0338ff4f58e603 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Thu, 6 Apr 2023 20:46:59 +0530 Subject: [PATCH 147/466] Remove unique constraint from order_key to prevent empty key conflict. --- .../changelog/fix-order_key_migration | 4 ++++ .../DataStores/Orders/OrdersTableDataStore.php | 2 +- .../PostsToOrdersMigrationControllerTest.php | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 plugins/woocommerce/changelog/fix-order_key_migration diff --git a/plugins/woocommerce/changelog/fix-order_key_migration b/plugins/woocommerce/changelog/fix-order_key_migration new file mode 100644 index 00000000000..6d6b7f5a067 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-order_key_migration @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Remove unique constraint from order_key, since orders can be created with emtpy order key, which conflicts with the constraint. diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php index 4050de796f1..1de11b44f65 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php @@ -2412,7 +2412,7 @@ CREATE TABLE $operational_data_table_name ( discount_total_amount decimal(26, 8) NULL, recorded_sales tinyint(1) NULL, UNIQUE KEY order_id (order_id), - UNIQUE KEY order_key (order_key) + KEY order_key (order_key) ) $collate; CREATE TABLE $meta_table ( id bigint(20) unsigned auto_increment primary key, diff --git a/plugins/woocommerce/tests/php/src/Database/Migrations/CustomOrderTable/PostsToOrdersMigrationControllerTest.php b/plugins/woocommerce/tests/php/src/Database/Migrations/CustomOrderTable/PostsToOrdersMigrationControllerTest.php index 1670e5444c3..739d518724f 100644 --- a/plugins/woocommerce/tests/php/src/Database/Migrations/CustomOrderTable/PostsToOrdersMigrationControllerTest.php +++ b/plugins/woocommerce/tests/php/src/Database/Migrations/CustomOrderTable/PostsToOrdersMigrationControllerTest.php @@ -746,4 +746,21 @@ WHERE order_id = {$order_id} AND meta_key = 'non_unique_key_1' AND meta_value in $this->assertEmpty( $errors ); } + + /** + * @testDox Test migration for multiple null order_key meta value. + */ + public function test_order_key_null_multiple() { + $order1 = OrderHelper::create_order(); + $order2 = OrderHelper::create_order(); + delete_post_meta( $order1->get_id(), '_order_key' ); + delete_post_meta( $order2->get_id(), '_order_key' ); + + $this->sut->migrate_order( $order1->get_id() ); + $this->sut->migrate_order( $order2->get_id() ); + + $errors = $this->sut->verify_migrated_orders( array( $order1->get_id(), $order2->get_id() ) ); + + $this->assertEmpty( $errors ); + } } From 7f00d211d11fc26982fc9c83670e44543d2c84e3 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Thu, 6 Apr 2023 20:48:11 +0530 Subject: [PATCH 148/466] Make migration more strict by removing IGNORE. --- .../src/Database/Migrations/MetaToCustomTableMigrator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php b/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php index 728cdaa70bb..71b0d3022a4 100644 --- a/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php +++ b/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php @@ -121,7 +121,7 @@ abstract class MetaToCustomTableMigrator extends TableMigrator { list( $value_sql, $column_sql ) = $this->generate_column_clauses( array_merge( $this->core_column_mapping, $this->meta_column_mapping ), $batch ); - return "INSERT IGNORE INTO $table (`$column_sql`) VALUES $value_sql;"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, -- $insert_query is hardcoded, $value_sql is already escaped. + return "INSERT INTO $table (`$column_sql`) VALUES $value_sql;"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, -- $insert_query is hardcoded, $value_sql is already escaped. } /** From 232a98317a211ec633c305df734e1516c077c44b Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Thu, 6 Apr 2023 20:48:45 +0530 Subject: [PATCH 149/466] Add changelog. --- plugins/woocommerce/changelog/fix-stricter_migration | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/fix-stricter_migration diff --git a/plugins/woocommerce/changelog/fix-stricter_migration b/plugins/woocommerce/changelog/fix-stricter_migration new file mode 100644 index 00000000000..6913259462b --- /dev/null +++ b/plugins/woocommerce/changelog/fix-stricter_migration @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Make migration more strict by removing IGNORE From 322639bb7efb4fe89cc651e9ab201b58e7e20d75 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Thu, 6 Apr 2023 21:06:38 +0530 Subject: [PATCH 150/466] Extra protection for empty order key orders. Some order can have order key set to empty string. This commit disallows fetching those orders via key at DB level (its already disallowed from interface). --- .../src/Internal/DataStores/Orders/OrdersTableDataStore.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php index 1de11b44f65..f9d15c1e53a 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php @@ -847,7 +847,7 @@ WHERE $wpdb->prepare( "SELECT {$orders_table}.id FROM {$orders_table} INNER JOIN {$op_table} ON {$op_table}.order_id = {$orders_table}.id - WHERE {$op_table}.order_key = %s", + WHERE {$op_table}.order_key = %s AND {$op_table}.order_key != ''", $order_key ) ); From 868fbbf23a3370c7244a82721e38aab7428f4fb6 Mon Sep 17 00:00:00 2001 From: Joshua T Flowers Date: Thu, 6 Apr 2023 10:13:48 -0700 Subject: [PATCH 151/466] Register product editor blocks server-side (#37339) * Move product block editor to new feature name * Register all blocks and block assets server-side * Add changelog entry * Disable phpcs rule for file_get_contents --- .../client/layout/controller.js | 2 +- .../client/typings/global.d.ts | 2 +- .../changelog/update-37241-block-registration | 4 + .../woocommerce/client/admin/config/core.json | 2 +- .../client/admin/config/development.json | 2 +- .../includes/admin/class-wc-admin-assets.php | 2 +- .../includes/admin/class-wc-admin-menus.php | 2 +- .../src/Admin/Features/Features.php | 2 +- .../ProductBlockEditor/BlockRegistry.php | 92 +++++++++++++++++++ .../Init.php} | 13 ++- 10 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 plugins/woocommerce/changelog/update-37241-block-registration create mode 100644 plugins/woocommerce/src/Admin/Features/ProductBlockEditor/BlockRegistry.php rename plugins/woocommerce/src/Admin/Features/{BlockEditorFeatureEnabled.php => ProductBlockEditor/Init.php} (89%) diff --git a/plugins/woocommerce-admin/client/layout/controller.js b/plugins/woocommerce-admin/client/layout/controller.js index 1863c17522e..b89dd7596d5 100644 --- a/plugins/woocommerce-admin/client/layout/controller.js +++ b/plugins/woocommerce-admin/client/layout/controller.js @@ -167,7 +167,7 @@ export const getPages = () => { } ); } - if ( window.wcAdminFeatures[ 'block-editor-feature-enabled' ] ) { + if ( window.wcAdminFeatures[ 'product-block-editor' ] ) { pages.push( { container: ProductPage, path: '/add-product', diff --git a/plugins/woocommerce-admin/client/typings/global.d.ts b/plugins/woocommerce-admin/client/typings/global.d.ts index 18d3d3c6d02..88b1a9564e8 100644 --- a/plugins/woocommerce-admin/client/typings/global.d.ts +++ b/plugins/woocommerce-admin/client/typings/global.d.ts @@ -9,7 +9,7 @@ declare global { wcAdminFeatures: { 'activity-panels': boolean; analytics: boolean; - 'block-editor-feature-enabled': boolean; + 'product-block-editor': boolean; coupons: boolean; 'customer-effort-score-tracks': boolean; homescreen: boolean; diff --git a/plugins/woocommerce/changelog/update-37241-block-registration b/plugins/woocommerce/changelog/update-37241-block-registration new file mode 100644 index 00000000000..bc69f48ac0f --- /dev/null +++ b/plugins/woocommerce/changelog/update-37241-block-registration @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Register product editor blocks server-side diff --git a/plugins/woocommerce/client/admin/config/core.json b/plugins/woocommerce/client/admin/config/core.json index f796d84a040..ddc2eb8f623 100644 --- a/plugins/woocommerce/client/admin/config/core.json +++ b/plugins/woocommerce/client/admin/config/core.json @@ -2,7 +2,7 @@ "features": { "activity-panels": true, "analytics": true, - "block-editor-feature-enabled": false, + "product-block-editor": false, "coupons": true, "customer-effort-score-tracks": true, "import-products-task": true, diff --git a/plugins/woocommerce/client/admin/config/development.json b/plugins/woocommerce/client/admin/config/development.json index b2511893a64..c6a95550b95 100644 --- a/plugins/woocommerce/client/admin/config/development.json +++ b/plugins/woocommerce/client/admin/config/development.json @@ -2,7 +2,7 @@ "features": { "activity-panels": true, "analytics": true, - "block-editor-feature-enabled": true, + "product-block-editor": true, "coupons": true, "customer-effort-score-tracks": true, "import-products-task": true, diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-assets.php b/plugins/woocommerce/includes/admin/class-wc-admin-assets.php index 251486de4d8..4c2ba397706 100644 --- a/plugins/woocommerce/includes/admin/class-wc-admin-assets.php +++ b/plugins/woocommerce/includes/admin/class-wc-admin-assets.php @@ -225,7 +225,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) : 'gateway_toggle' => wp_create_nonce( 'woocommerce-toggle-payment-gateway-enabled' ), ), 'urls' => array( - 'add_product' => Features::is_enabled( 'new-product-management-experience' ) || Features::is_enabled( 'block-editor-feature-enabled' ) ? esc_url_raw( admin_url( 'admin.php?page=wc-admin&path=/add-product' ) ) : null, + 'add_product' => Features::is_enabled( 'new-product-management-experience' ) || Features::is_enabled( 'product-block-editor' ) ? esc_url_raw( admin_url( 'admin.php?page=wc-admin&path=/add-product' ) ) : null, 'import_products' => current_user_can( 'import' ) ? esc_url_raw( admin_url( 'edit.php?post_type=product&page=product_importer' ) ) : null, 'export_products' => current_user_can( 'export' ) ? esc_url_raw( admin_url( 'edit.php?post_type=product&page=product_exporter' ) ) : null, ), diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-menus.php b/plugins/woocommerce/includes/admin/class-wc-admin-menus.php index 1f3174c4dc9..79d4eb44045 100644 --- a/plugins/woocommerce/includes/admin/class-wc-admin-menus.php +++ b/plugins/woocommerce/includes/admin/class-wc-admin-menus.php @@ -427,7 +427,7 @@ class WC_Admin_Menus { * Maybe add new management product experience. */ public function maybe_add_new_product_management_experience() { - if ( Features::is_enabled( 'new-product-management-experience' ) || Features::is_enabled( 'block-editor-feature-enabled' ) ) { + if ( Features::is_enabled( 'new-product-management-experience' ) || Features::is_enabled( 'product-block-editor' ) ) { global $submenu; if ( isset( $submenu['edit.php?post_type=product'][10] ) ) { // Disable phpcs since we need to override submenu classes. diff --git a/plugins/woocommerce/src/Admin/Features/Features.php b/plugins/woocommerce/src/Admin/Features/Features.php index 681b322bb6c..0c7d36b845e 100644 --- a/plugins/woocommerce/src/Admin/Features/Features.php +++ b/plugins/woocommerce/src/Admin/Features/Features.php @@ -40,7 +40,7 @@ class Features { protected static $beta_features = array( 'navigation', 'new-product-management-experience', - 'block-editor-feature-enabled', + 'product-block-editor', 'settings', ); diff --git a/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/BlockRegistry.php b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/BlockRegistry.php new file mode 100644 index 00000000000..0271e4cf7ac --- /dev/null +++ b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/BlockRegistry.php @@ -0,0 +1,92 @@ +register_block( $block_name ); + } + } + + /** + * Get the block name without the "woocommerce/" prefix. + * + * @param string $block_name Block name. + * + * @return string + */ + public function remove_block_prefix( $block_name ) { + if ( 0 === strpos( $block_name, 'woocommerce/' ) ) { + return substr_replace( $block_name, '', 0, strlen( 'woocommerce/' ) ); + } + + return $block_name; + } + + /** + * Register a single block. + * + * @param string $block_name Block name. + * + * @return WP_Block_Type|false The registered block type on success, or false on failure. + */ + public function register_block( $block_name ) { + $block_name = $this->remove_block_prefix( $block_name ); + $block_json_file = $this->get_file_path( $block_name . '/block.json' ); + + if ( ! file_exists( $block_json_file ) ) { + return false; + } + + // phpcs:disable WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents + $metadata = json_decode( file_get_contents( $block_json_file ), true ); + if ( ! is_array( $metadata ) || ! $metadata['name'] ) { + return false; + } + + $registry = \WP_Block_Type_Registry::get_instance(); + + if ( $registry->is_registered( $metadata['name'] ) ) { + $registry->unregister( $metadata['name'] ); + } + + return register_block_type_from_metadata( $block_json_file ); + } + +} diff --git a/plugins/woocommerce/src/Admin/Features/BlockEditorFeatureEnabled.php b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php similarity index 89% rename from plugins/woocommerce/src/Admin/Features/BlockEditorFeatureEnabled.php rename to plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php index bdd41ab0a84..b907226e10f 100644 --- a/plugins/woocommerce/src/Admin/Features/BlockEditorFeatureEnabled.php +++ b/plugins/woocommerce/src/Admin/Features/ProductBlockEditor/Init.php @@ -1,21 +1,22 @@ register_product_blocks(); } } From 2396eb8e4c0be8363f473e02c8da5ac6e2b5f410 Mon Sep 17 00:00:00 2001 From: Barry Hughes <3594411+barryhughes@users.noreply.github.com> Date: Thu, 6 Apr 2023 12:24:35 -0700 Subject: [PATCH 152/466] Add link to GitHub Discussions per DevAdv feedback. --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 6e472c962d1..03c09011544 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -48,4 +48,4 @@ If you are contributing code to our (Javascript-driven) Gutenberg blocks, please The best place to submit feature requests is over on our [dedicated feature request page](https://woocommerce.com/feature-requests/woocommerce/). You can easily search and vote for existing requests, or create new requests if necessary. -Alternatively, if you wish to propose a straightforward technical enhancement that is unlikely to require much discussion, you can [open a new issue](https://github.com/woocommerce/woocommerce/issues/new?assignees=&labels=type%3A+enhancement%2Cstatus%3A+awaiting+triage&template=2-enhancement.yml&title=%5BEnhancement%5D%3A+) right here on GitHub. +Alternatively, if you wish to propose a straightforward technical enhancement that is unlikely to require much discussion, you can [open a new issue](https://github.com/woocommerce/woocommerce/issues/new?assignees=&labels=type%3A+enhancement%2Cstatus%3A+awaiting+triage&template=2-enhancement.yml&title=%5BEnhancement%5D%3A+) right here on GitHub and, for any that may require more discussion, consider syncing with us during office hours or publishing a thread on [GitHub Discussions](https://github.com/woocommerce/woocommerce/discussions). From e0327e32232221e42a4d864bdd916fc93c80718b Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Fri, 7 Apr 2023 18:06:55 +0530 Subject: [PATCH 153/466] Fixup in unit test. --- .../class-wc-abstract-order-test.php | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php b/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php index 3b2cc7a9c46..f0e9b60cd6e 100644 --- a/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php +++ b/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php @@ -272,11 +272,16 @@ class WC_Abstract_Order_Test extends WC_Unit_Test_Case { /** * Test for get_discount_to_display which must return a value - * with and without tax whatever the setting of the options + * with and without tax whatever the setting of the options. * * Issue :https://github.com/woocommerce/woocommerce/issues/36794 */ public function test_get_discount_to_display() { + update_option( 'woocommerce_calc_taxes', 'yes' ); + update_option( 'woocommerce_prices_include_tax', 'no' ); + update_option( 'woocommerce_currency', 'USD' ); + update_option( 'woocommerce_tax_display_cart', 'incl' ); + // Set dummy data $tax_rate = array( 'tax_rate_country' => '', @@ -289,7 +294,7 @@ class WC_Abstract_Order_Test extends WC_Unit_Test_Case { WC_Tax::_insert_tax_rate( $tax_rate ); $coupon = WC_Helper_Coupon::create_coupon(); - $product = WC_Helper_Product::create_simple_product(); + $product = WC_Helper_Product::create_simple_product( true, array( 'price' => 10 ) ); $order = new WC_Order(); $order->add_product( $product ); @@ -297,16 +302,8 @@ class WC_Abstract_Order_Test extends WC_Unit_Test_Case { $order->calculate_totals(); $order->save(); - // Test - update_option( 'woocommerce_currency', 'USD' ); - update_option( 'woocommerce_currency_pos', 'right' ); - update_option( 'woocommerce_price_thousand_sep', '' ); - update_option( 'woocommerce_price_decimal_sep', '.' ); - update_option( 'woocommerce_price_num_decimals', 2 ); - - update_option( 'woocommerce_tax_display_cart', 'incl' ); - $this->assertEquals( '1.00$', strip_tags( $order->get_discount_to_display( 'excl' ) ) ); - $this->assertEquals( '1.20$', strip_tags( $order->get_discount_to_display( 'incl' ) ) ); + $this->assertEquals( wc_price( 1, array( 'currency' => 'USD' ) ), $order->get_discount_to_display( 'excl' ) ); + $this->assertEquals( wc_price( 1.20, array( 'currency' => 'USD' ) ), $order->get_discount_to_display( 'incl' ) ); } } From 835d089fb745d7abe0a2d67809007e2e1f5d1eda Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Fri, 7 Apr 2023 18:11:32 +0530 Subject: [PATCH 154/466] Add changelog. --- plugins/woocommerce/changelog/pr-36885 | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/pr-36885 diff --git a/plugins/woocommerce/changelog/pr-36885 b/plugins/woocommerce/changelog/pr-36885 new file mode 100644 index 00000000000..ed20c22ae98 --- /dev/null +++ b/plugins/woocommerce/changelog/pr-36885 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Remove double checking for woocommerce_tax_display_cart filter. From 40d823df5c5a07cbf0d764d1c14df26742067c99 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Fri, 7 Apr 2023 18:31:21 +0530 Subject: [PATCH 155/466] Coding standard fix. --- .../woocommerce/includes/abstracts/abstract-wc-order.php | 6 ++++++ .../php/includes/abstracts/class-wc-abstract-order-test.php | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php index e077c36055e..717800b4c4c 100644 --- a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php +++ b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php @@ -2185,6 +2185,12 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { */ public function get_discount_to_display( $tax_display = '' ) { $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' ); + + /** + * Filter the discount amount to display. + * + * @since 2.7.0. + */ return apply_filters( 'woocommerce_order_discount_to_display', wc_price( $this->get_total_discount( 'excl' === $tax_display ), array( 'currency' => $this->get_currency() ) ), $this ); } diff --git a/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php b/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php index 23e0f9a1f79..b164ec95a5f 100644 --- a/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php +++ b/plugins/woocommerce/tests/php/includes/abstracts/class-wc-abstract-order-test.php @@ -282,7 +282,7 @@ class WC_Abstract_Order_Test extends WC_Unit_Test_Case { update_option( 'woocommerce_currency', 'USD' ); update_option( 'woocommerce_tax_display_cart', 'incl' ); - // Set dummy data + // Set dummy data. $tax_rate = array( 'tax_rate_country' => '', 'tax_rate_state' => '', From ad565bd8534ed6a5546e91b2f411fba38338769c Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Fri, 7 Apr 2023 15:40:44 -0700 Subject: [PATCH 156/466] Add table name methods to OrderUtil Adds methods to determine the names of the database tables currently being used for orders and order meta, without needing to make a separate check first to see if HPOS is enabled and in use. --- .../woocommerce/src/Utilities/OrderUtil.php | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/plugins/woocommerce/src/Utilities/OrderUtil.php b/plugins/woocommerce/src/Utilities/OrderUtil.php index 1a609058a01..e341ef8ef50 100644 --- a/plugins/woocommerce/src/Utilities/OrderUtil.php +++ b/plugins/woocommerce/src/Utilities/OrderUtil.php @@ -7,7 +7,7 @@ namespace Automattic\WooCommerce\Utilities; use Automattic\WooCommerce\Caches\OrderCacheController; use Automattic\WooCommerce\Internal\Admin\Orders\PageController; -use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController; +use Automattic\WooCommerce\Internal\DataStores\Orders\{ CustomOrdersTableController, OrdersTableDataStore }; use Automattic\WooCommerce\Internal\Features\FeaturesController; use Automattic\WooCommerce\Internal\Utilities\COTMigrationUtil; use WC_Order; @@ -134,4 +134,36 @@ final class OrderUtil { public static function get_order_admin_new_url() : string { return wc_get_container()->get( PageController::class )->get_new_page_url(); } + + /** + * Get the name of the database table that's currently in use for orders. + * + * @return string + */ + public static function get_table_for_orders() { + if ( self::custom_orders_table_usage_is_enabled() ) { + $table_name = OrdersTableDataStore::get_orders_table_name(); + } else { + global $wpdb; + $table_name = $wpdb->posts; + } + + return $table_name; + } + + /** + * Get the name of the database table that's currently in use for orders. + * + * @return string + */ + public static function get_table_for_order_meta() { + if ( self::custom_orders_table_usage_is_enabled() ) { + $table_name = OrdersTableDataStore::get_meta_table_name(); + } else { + global $wpdb; + $table_name = $wpdb->postmeta; + } + + return $table_name; + } } From 208fa5ea00af4c22494fe55317d646e77cf91e69 Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Fri, 7 Apr 2023 17:21:12 -0700 Subject: [PATCH 157/466] Add unit tests --- .../tests/php/src/Utilities/OrderUtilTest.php | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 plugins/woocommerce/tests/php/src/Utilities/OrderUtilTest.php diff --git a/plugins/woocommerce/tests/php/src/Utilities/OrderUtilTest.php b/plugins/woocommerce/tests/php/src/Utilities/OrderUtilTest.php new file mode 100644 index 00000000000..a1726b28992 --- /dev/null +++ b/plugins/woocommerce/tests/php/src/Utilities/OrderUtilTest.php @@ -0,0 +1,76 @@ +prev_cot_state = OrderUtil::custom_orders_table_usage_is_enabled(); + } + + /** + * Restore the COT state after the test. + * + * @return void + */ + public function tearDown(): void { + OrderHelper::toggle_cot( $this->prev_cot_state ); + parent::tearDown(); + } + + /** + * @testdox `get_table_for_orders` should return the name of the posts table when HPOS is not in use. + */ + public function test_get_table_for_orders_posts() { + OrderHelper::toggle_cot( false ); + + $table_name = OrderUtil::get_table_for_orders(); + $this->assertEquals( 'wptests_posts', $table_name ); + } + + /** + * @testdox `get_table_for_orders` should return the name of the orders table when HPOS is in use. + */ + public function test_get_table_for_orders_hpos() { + OrderHelper::toggle_cot( true ); + + $table_name = OrderUtil::get_table_for_orders(); + $this->assertEquals( 'wptests_wc_orders', $table_name ); + } + + /** + * @testdox `get_table_for_order_meta` should return the name of the postmeta table when HPOS is not in use. + */ + public function test_get_table_for_order_meta_posts() { + OrderHelper::toggle_cot( false ); + + $table_name = OrderUtil::get_table_for_order_meta(); + $this->assertEquals( 'wptests_postmeta', $table_name ); + } + + /** + * @testdox `get_table_for_order_meta` should return the name of the orders meta table when HPOS is in use. + */ + public function test_get_table_for_order_meta_hpos() { + OrderHelper::toggle_cot( true ); + + $table_name = OrderUtil::get_table_for_order_meta(); + $this->assertEquals( 'wptests_wc_orders_meta', $table_name ); + } +} From 62ceb8e08b0bf559998a9c4d627aa41d8975f43a Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Fri, 7 Apr 2023 17:30:31 -0700 Subject: [PATCH 158/466] Add changelog file --- plugins/woocommerce/changelog/add-hpos-table-name-util | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/add-hpos-table-name-util diff --git a/plugins/woocommerce/changelog/add-hpos-table-name-util b/plugins/woocommerce/changelog/add-hpos-table-name-util new file mode 100644 index 00000000000..164749112f1 --- /dev/null +++ b/plugins/woocommerce/changelog/add-hpos-table-name-util @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add methods to OrderUtil to get the names of order database tables From 0bc31867a49ab3e06eb3af0b96702988ae128a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CChris?= Date: Mon, 10 Apr 2023 14:25:19 +0300 Subject: [PATCH 159/466] Print block-based css classes only on fse themes --- plugins/woocommerce/includes/wc-conditional-functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/woocommerce/includes/wc-conditional-functions.php b/plugins/woocommerce/includes/wc-conditional-functions.php index 64651baab16..c5fc0c261e8 100644 --- a/plugins/woocommerce/includes/wc-conditional-functions.php +++ b/plugins/woocommerce/includes/wc-conditional-functions.php @@ -527,7 +527,7 @@ function wc_current_theme_supports_woocommerce_or_fse() { /** * Given an element name, returns a class name. * - * If the WP-related function is not defined, return empty string. + * If the WP-related function is not defined or current theme is not a FSE theme, return empty string. * * @param string $element The name of the element. * @@ -535,7 +535,7 @@ function wc_current_theme_supports_woocommerce_or_fse() { * @return string */ function wc_wp_theme_get_element_class_name( $element ) { - if ( function_exists( 'wp_theme_get_element_class_name' ) ) { + if ( wc_current_theme_is_fse_theme() && function_exists( 'wp_theme_get_element_class_name' ) ) { return wp_theme_get_element_class_name( $element ); } From 0863a76ce180aed18404c3618c594389af3c0ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CChris?= Date: Mon, 10 Apr 2023 14:27:20 +0300 Subject: [PATCH 160/466] Add changelog --- .../changelog/enchancement-37630-theme-element-class-name | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/enchancement-37630-theme-element-class-name diff --git a/plugins/woocommerce/changelog/enchancement-37630-theme-element-class-name b/plugins/woocommerce/changelog/enchancement-37630-theme-element-class-name new file mode 100644 index 00000000000..67c83314bf7 --- /dev/null +++ b/plugins/woocommerce/changelog/enchancement-37630-theme-element-class-name @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Print blocks-based CSS classes only when a FSE theme is used From 5dda0aac48b7fd95f86332a7fc68a8839b4e2298 Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Mon, 10 Apr 2023 08:34:38 -0400 Subject: [PATCH 161/466] Beta Tester: Update WCA Test Helper Features list (#37483) * Alphabetize features list * Use toggles instead of button for features * Changelog --- .../update-test-helper-features-list | 4 ++ .../src/features/index.js | 50 +++++++------------ 2 files changed, 22 insertions(+), 32 deletions(-) create mode 100644 plugins/woocommerce-beta-tester/changelog/update-test-helper-features-list diff --git a/plugins/woocommerce-beta-tester/changelog/update-test-helper-features-list b/plugins/woocommerce-beta-tester/changelog/update-test-helper-features-list new file mode 100644 index 00000000000..eacb1ddb3c4 --- /dev/null +++ b/plugins/woocommerce-beta-tester/changelog/update-test-helper-features-list @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Alphabetize the features list, and use toggle controls instead of buttons. diff --git a/plugins/woocommerce-beta-tester/src/features/index.js b/plugins/woocommerce-beta-tester/src/features/index.js index f89defecc1c..667f817de85 100644 --- a/plugins/woocommerce-beta-tester/src/features/index.js +++ b/plugins/woocommerce-beta-tester/src/features/index.js @@ -2,7 +2,7 @@ * External dependencies */ import { useDispatch, useSelect } from '@wordpress/data'; -import { Button } from '@wordpress/components'; +import { Button, ToggleControl } from '@wordpress/components'; /** * Internal dependencies @@ -21,6 +21,8 @@ function Features() { const { toggleFeature, resetModifiedFeatures } = useDispatch( STORE_KEY ); + const sortedFeatureNames = Object.keys( features ).sort(); + return (

    @@ -34,37 +36,21 @@ function Features() { Reset to defaults

    -
    get( HtmlSanitizer::class )->sanitize( $total['value'] ) : wp_kses_post( $total['value'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
    get( HtmlSanitizer::class )->sanitize( $total['value'] ) : wp_kses_post( $total['value'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
    - - - - - - - - - { Object.keys( features ).map( ( feature_name ) => { - return ( - - - - - - ); - } ) } - -
    Feature NameEnabled?Toggle
    - { feature_name } - { features[ feature_name ].toString() } - -
    +

      + { sortedFeatureNames.map( ( feature_name ) => { + return ( +
    • + { + toggleFeature( feature_name ); + } } + /> +
    • + ); + } ) } +
    ); } From a054ddbf50383147ab844ab1d8eef9658f7f470a Mon Sep 17 00:00:00 2001 From: Matt Sherman Date: Mon, 10 Apr 2023 08:36:13 -0400 Subject: [PATCH 162/466] Beta Tester: Update WCA Test Helper option modal to show full option name (#37495) * Adjust options modal so that option name is not cut off * Changelog --- .../changelog/fix-test-helper-option-modal | 4 ++ .../woocommerce-beta-tester/src/index.scss | 32 +++++++++-- .../src/options/OptionEditor.js | 48 ---------------- .../src/options/index.js | 24 +++----- .../src/options/option-modal.js | 57 +++++++++++++++++++ 5 files changed, 95 insertions(+), 70 deletions(-) create mode 100644 plugins/woocommerce-beta-tester/changelog/fix-test-helper-option-modal delete mode 100644 plugins/woocommerce-beta-tester/src/options/OptionEditor.js create mode 100644 plugins/woocommerce-beta-tester/src/options/option-modal.js diff --git a/plugins/woocommerce-beta-tester/changelog/fix-test-helper-option-modal b/plugins/woocommerce-beta-tester/changelog/fix-test-helper-option-modal new file mode 100644 index 00000000000..ae1bfdb42bc --- /dev/null +++ b/plugins/woocommerce-beta-tester/changelog/fix-test-helper-option-modal @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Adjust option modal so that longer option names are fully shown. diff --git a/plugins/woocommerce-beta-tester/src/index.scss b/plugins/woocommerce-beta-tester/src/index.scss index b051363c9f4..d213c21ec37 100644 --- a/plugins/woocommerce-beta-tester/src/index.scss +++ b/plugins/woocommerce-beta-tester/src/index.scss @@ -50,13 +50,33 @@ } } -.wca-test-helper-option-editor { - width: 100%; - height: 300px; -} +.wca-test-helper-option-modal-content { + display: flex; + flex-direction: column; -.wca-test-helper-edit-btn-save { - float: right; + label { + margin-bottom: 5px; + } + + output, textarea { + margin-bottom: 10px; + } + + #wca-test-helper-option-modal-name { + font-size: 1.2em; + font-weight: bold; + } + + #wca-test-helper-option-editor { + height: 300px; + } + + .wca-test-helper-option-modal-buttons { + display: flex; + justify-content: flex-end; + margin-top: 10px; + gap: 10px; + } } #wc-admin-test-helper-tools, diff --git a/plugins/woocommerce-beta-tester/src/options/OptionEditor.js b/plugins/woocommerce-beta-tester/src/options/OptionEditor.js deleted file mode 100644 index 5784b179fd3..00000000000 --- a/plugins/woocommerce-beta-tester/src/options/OptionEditor.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * External dependencies - */ -import { useEffect, useState } from '@wordpress/element'; -import PropTypes from 'prop-types'; -import { Button } from '@wordpress/components'; - -const OptionEditor = ( props ) => { - const [ value, setValue ] = useState( props.option.content ); - - useEffect( () => { - setValue( props.option.content ); - }, [ props.option ] ); - - const handleChange = ( event ) => { - setValue( event.target.value ); - }; - - const handleSave = () => { - props.onSave( props.option.name, value ); - }; - - return ( - <> - - -
    - - ); -}; - -OptionEditor.propTypes = { - option: PropTypes.object.isRequired, - onSave: PropTypes.func.isRequired, -}; - -export default OptionEditor; diff --git a/plugins/woocommerce-beta-tester/src/options/index.js b/plugins/woocommerce-beta-tester/src/options/index.js index a85a37afcea..c4eae7e908d 100644 --- a/plugins/woocommerce-beta-tester/src/options/index.js +++ b/plugins/woocommerce-beta-tester/src/options/index.js @@ -3,14 +3,14 @@ */ import { withDispatch, withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; -import { Modal, Notice } from '@wordpress/components'; +import { Notice } from '@wordpress/components'; import { useState } from '@wordpress/element'; /** * Internal dependencies */ import { STORE_KEY } from './data/constants'; -import { default as OptionEditor } from './OptionEditor'; +import { OptionModal } from './option-modal'; import './data'; function shorten( input ) { @@ -131,17 +131,13 @@ function Options( { return ( <> { isEditModalOpen && ( - { setEditModalOpen( false ); } } - > - - + option={ editingOption } + onSave={ handleSaveOption } + /> ) }
    { notice.message.length > 0 && ( @@ -224,12 +220,8 @@ function Options( { export default compose( withSelect( ( select ) => { - const { - getOptions, - getOptionForEditing, - getNotice, - isLoading, - } = select( STORE_KEY ); + const { getOptions, getOptionForEditing, getNotice, isLoading } = + select( STORE_KEY ); const options = getOptions(); const editingOption = getOptionForEditing(); const notice = getNotice(); diff --git a/plugins/woocommerce-beta-tester/src/options/option-modal.js b/plugins/woocommerce-beta-tester/src/options/option-modal.js new file mode 100644 index 00000000000..6bbca0c3567 --- /dev/null +++ b/plugins/woocommerce-beta-tester/src/options/option-modal.js @@ -0,0 +1,57 @@ +/** + * External dependencies + */ +import { useEffect, useState } from '@wordpress/element'; +import PropTypes from 'prop-types'; +import { Button, Modal } from '@wordpress/components'; + +export const OptionModal = ( props ) => { + const [ value, setValue ] = useState( props.option.content ); + + useEffect( () => { + setValue( props.option.content ); + }, [ props.option ] ); + + const handleChange = ( event ) => { + setValue( event.target.value ); + }; + + const handleSave = () => { + props.onSave( props.option.name, value ); + }; + + return ( + +
    + + + { props.option.name } + + + +
    + + +
    +
    +
    + ); +}; + +OptionModal.propTypes = { + option: PropTypes.object.isRequired, + onRequestClose: PropTypes.func.isRequired, + onSave: PropTypes.func.isRequired, +}; From 2c626fe854c825bde40588dee816ad26aff2876a Mon Sep 17 00:00:00 2001 From: "Jorge A. Torres" Date: Mon, 10 Apr 2023 14:11:08 -0500 Subject: [PATCH 163/466] Add auto-draft to orders list table --- .../woocommerce/src/Internal/Admin/Orders/ListTable.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/woocommerce/src/Internal/Admin/Orders/ListTable.php b/plugins/woocommerce/src/Internal/Admin/Orders/ListTable.php index 02745c54589..c105b4e4ce1 100644 --- a/plugins/woocommerce/src/Internal/Admin/Orders/ListTable.php +++ b/plugins/woocommerce/src/Internal/Admin/Orders/ListTable.php @@ -471,7 +471,7 @@ class ListTable extends WP_List_Table { $view_counts[ $slug ] = $total_in_status; } - if ( ( get_post_status_object( $slug ) )->show_in_admin_all_list ) { + if ( ( get_post_status_object( $slug ) )->show_in_admin_all_list && 'auto-draft' !== $slug ) { $all_count += $total_in_status; } } @@ -550,8 +550,9 @@ class ListTable extends WP_List_Table { array_merge( wc_get_order_statuses(), array( - 'trash' => ( get_post_status_object( 'trash' ) )->label, - 'draft' => ( get_post_status_object( 'draft' ) )->label, + 'trash' => ( get_post_status_object( 'trash' ) )->label, + 'draft' => ( get_post_status_object( 'draft' ) )->label, + 'auto-draft' => ( get_post_status_object( 'auto-draft' ) )->label, ) ), array_flip( get_post_stati( array( 'show_in_admin_status_list' => true ) ) ) @@ -916,7 +917,7 @@ class ListTable extends WP_List_Table { } // Gracefully handle legacy statuses. - if ( in_array( $order->get_status(), array( 'trash', 'draft' ), true ) ) { + if ( in_array( $order->get_status(), array( 'trash', 'draft', 'auto-draft' ), true ) ) { $status_name = ( get_post_status_object( $order->get_status() ) )->label; } else { $status_name = wc_get_order_status_name( $order->get_status() ); From 9d60bf90ed4277238b9049765bee41c3817a4af2 Mon Sep 17 00:00:00 2001 From: "Jorge A. Torres" Date: Mon, 10 Apr 2023 14:13:00 -0500 Subject: [PATCH 164/466] =?UTF-8?q?Create=20orders=20as=20=E2=80=98auto-dr?= =?UTF-8?q?aft=E2=80=99=20instead=20of=20=E2=80=98pending=E2=80=99=20in=20?= =?UTF-8?q?the=20admin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Internal/Admin/Orders/PageController.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php b/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php index b2e93628844..7f6109a2dbd 100644 --- a/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php +++ b/plugins/woocommerce/src/Internal/Admin/Orders/PageController.php @@ -286,9 +286,14 @@ class PageController { $this->order = new $order_class_name(); $this->order->set_object_read( false ); - $this->order->set_status( 'pending' ); + $this->order->set_status( 'auto-draft' ); $this->order->save(); + // Schedule auto-draft cleanup. We re-use the WP event here on purpose. + if ( ! wp_next_scheduled( 'wp_scheduled_auto_draft_delete' ) ) { + wp_schedule_event( time(), 'daily', 'wp_scheduled_auto_draft_delete' ); + } + $theorder = $this->order; } From dec3dda375788048fcddf5c66b318eb150e71d9d Mon Sep 17 00:00:00 2001 From: "Jorge A. Torres" Date: Mon, 10 Apr 2023 14:13:12 -0500 Subject: [PATCH 165/466] Periodically delete auto-draft orders older than a week --- .../DataStores/Orders/DataSynchronizer.php | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php b/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php index 193009492ec..d407ef946d4 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php @@ -79,6 +79,8 @@ class DataSynchronizer implements BatchProcessorInterface { self::add_action( 'deleted_post', array( $this, 'handle_deleted_post' ), 10, 2 ); self::add_action( 'woocommerce_new_order', array( $this, 'handle_updated_order' ), 100 ); self::add_action( 'woocommerce_update_order', array( $this, 'handle_updated_order' ), 100 ); + self::add_action( 'wp_scheduled_auto_draft_delete', array( $this, 'delete_auto_draft_orders' ) ); + self::add_filter( 'woocommerce_feature_description_tip', array( $this, 'handle_feature_description_tip' ), 10, 3 ); } @@ -467,6 +469,45 @@ ORDER BY orders.id ASC } } + /** + * Handles deletion of auto-draft orders in sync with WP's own auto-draft deletion. + * + * @since 7.7.0 + * + * @return void + */ + private function delete_auto_draft_orders() { + if ( ! $this->custom_orders_table_is_authoritative() ) { + return; + } + + // Fetch auto-draft orders older than 1 week. + $to_delete = wc_get_orders( + array( + 'date_query' => array( + array( + 'column' => 'date_created', + 'before' => '-1 week', + ), + ), + 'orderby' => 'date', + 'order' => 'ASC', + 'status' => 'auto-draft', + ) + ); + + foreach ( $to_delete as $order ) { + $order->delete( true ); + } + + /** + * Fires after schedueld deletion of auto-draft orders has been completed. + * + * @since 7.7.0 + */ + do_action( 'woocommerce_scheduled_auto_draft_delete' ); + } + /** * Handle the 'woocommerce_feature_description_tip' filter. * From 4873f7c2c5457e16df8a78f3cf181527d662a202 Mon Sep 17 00:00:00 2001 From: "Jorge A. Torres" Date: Mon, 10 Apr 2023 14:13:22 -0500 Subject: [PATCH 166/466] Test auto-draft deletion --- .../Orders/DataSynchronizerTests.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php index 6d1ac547a35..9fc3fe77805 100644 --- a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php +++ b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php @@ -28,6 +28,7 @@ class DataSynchronizerTests extends WC_Unit_Test_Case { remove_filter( 'query', array( $this, '_drop_temporary_tables' ) ); OrderHelper::delete_order_custom_tables(); // We need this since non-temporary tables won't drop automatically. OrderHelper::create_order_custom_table_if_not_exist(); + OrderHelper::toggle_cot( false ); $this->sut = wc_get_container()->get( DataSynchronizer::class ); $features_controller = wc_get_container()->get( Featurescontroller::class ); $features_controller->change_feature_enable( 'custom_order_tables', true ); @@ -311,4 +312,43 @@ class DataSynchronizerTests extends WC_Unit_Test_Case { $this->assertEquals( $order2->get_id(), $orders_to_migrate[0] ); $this->assertEquals( $max_id + 1, $orders_to_migrate[1] ); } + + /** + * Tests that auto-draft orders older than 1 week are automatically deleted when WP does the same for posts. + * + * @return void + */ + public function test_auto_draft_deletion(): void { + OrderHelper::toggle_cot( true ); + + $order1 = new \WC_Order(); + $order1->set_status( 'auto-draft' ); + $order1->set_date_created( strtotime( '-10 days' ) ); + $order1->save(); + + $order2 = new \WC_Order(); + $order2->set_status( 'auto-draft' ); + $order2->save(); + + $order3 = new \WC_Order(); + $order3->set_status( 'processing' ); + $order3->save(); + + // Run WP's auto-draft delete. + do_action( 'wp_scheduled_auto_draft_delete' ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.HookCommentWrongStyle + + $orders = wc_get_orders( + array( + 'status' => 'all', + 'limit' => -1, + 'return' => 'ids', + ) + ); + + // Confirm that only $order1 is deleted when the action runs but the other orders remain intact. + $this->assertContains( $order2->get_id(), $orders ); + $this->assertContains( $order3->get_id(), $orders ); + $this->assertNotContains( $order1->get_id(), $orders ); + } + } From 4e16e6117b317b0cff484a025a01abe2775a565a Mon Sep 17 00:00:00 2001 From: "Jorge A. Torres" Date: Mon, 10 Apr 2023 14:13:28 -0500 Subject: [PATCH 167/466] Add changelog --- plugins/woocommerce/changelog/fix-36749 | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/fix-36749 diff --git a/plugins/woocommerce/changelog/fix-36749 b/plugins/woocommerce/changelog/fix-36749 new file mode 100644 index 00000000000..3dbb28e7f2b --- /dev/null +++ b/plugins/woocommerce/changelog/fix-36749 @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +Create orders as 'auto-draft' by default in admin. From 18d00cadd0e0c851d5a41ec54a76b8f7b5b5647a Mon Sep 17 00:00:00 2001 From: "Jorge A. Torres" Date: Mon, 10 Apr 2023 15:11:03 -0500 Subject: [PATCH 168/466] =?UTF-8?q?Consider=20=E2=80=98auto-draft=E2=80=99?= =?UTF-8?q?=20the=20same=20as=20=E2=80=98pending=E2=80=99=20for=20orders?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This for backwards compat with CPT --- plugins/woocommerce/includes/abstracts/abstract-wc-order.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php index 717800b4c4c..e4afa9c5be7 100644 --- a/plugins/woocommerce/includes/abstracts/abstract-wc-order.php +++ b/plugins/woocommerce/includes/abstracts/abstract-wc-order.php @@ -638,7 +638,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { } // If the old status is set but unknown (e.g. draft) assume its pending for action usage. - if ( $old_status && ! in_array( 'wc-' . $old_status, $this->get_valid_statuses(), true ) && ! in_array( $old_status, $status_exceptions, true ) ) { + if ( $old_status && ( ( ! in_array( 'wc-' . $old_status, $this->get_valid_statuses(), true ) && ! in_array( $old_status, $status_exceptions, true ) ) || 'auto-draft' === $old_status ) ) { $old_status = 'pending'; } } From 5d68259480fba10094c242c2e1286c81d79d568a Mon Sep 17 00:00:00 2001 From: Nathan Silveira Date: Mon, 10 Apr 2023 17:44:51 -0300 Subject: [PATCH 169/466] Add different confirmation message when attribute is used in a variation (#37527) * Add different confirmation message when attribute is used in a variation * Update logic to only consider checkbox checked value when prompting message --- .../changelog/update-confirm-remove-attr | 4 + .../legacy/js/admin/meta-boxes-product.js | 83 ++++++++++--------- .../includes/admin/class-wc-admin-assets.php | 1 + 3 files changed, 51 insertions(+), 37 deletions(-) create mode 100644 plugins/woocommerce/changelog/update-confirm-remove-attr diff --git a/plugins/woocommerce/changelog/update-confirm-remove-attr b/plugins/woocommerce/changelog/update-confirm-remove-attr new file mode 100644 index 00000000000..6b39ccb9d9a --- /dev/null +++ b/plugins/woocommerce/changelog/update-confirm-remove-attr @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Show different error message when deleting an attribute used in variations diff --git a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product.js b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product.js index 859901b27fa..efb439fb50e 100644 --- a/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product.js +++ b/plugins/woocommerce/client/legacy/js/admin/meta-boxes-product.js @@ -628,47 +628,56 @@ jQuery( function ( $ ) { } ); - $( '.product_attributes' ).on( 'click', '.remove_row', function () { - if ( window.confirm( woocommerce_admin_meta_boxes.remove_attribute ) ) { + $( '#product_attributes' ).on( + 'click', + '.product_attributes .remove_row', + function () { var $parent = $( this ).parent().parent(); + var confirmMessage = $parent + .find( 'input[name^="attribute_variation"]' ) + .is( ':checked' ) + ? woocommerce_admin_meta_boxes.i18n_remove_used_attribute_confirmation_message + : woocommerce_admin_meta_boxes.remove_attribute; - if ( $parent.is( '.taxonomy' ) ) { - $parent.find( 'select, input[type=text]' ).val( '' ); - $parent.hide(); - $( 'select.attribute_taxonomy' ) - .find( - 'option[value="' + $parent.data( 'taxonomy' ) + '"]' - ) - .prop( 'disabled', false ); - selectedAttributes = selectedAttributes.filter( - ( attr ) => attr !== $parent.data( 'taxonomy' ) - ); - $( 'select.wc-attribute-search' ).data( - 'disabled-items', - selectedAttributes - ); - } else { - $parent.find( 'select, input[type=text]' ).val( '' ); - $parent.hide(); - attribute_row_indexes(); + if ( window.confirm( confirmMessage ) ) { + if ( $parent.is( '.taxonomy' ) ) { + $parent.find( 'select, input[type=text]' ).val( '' ); + $parent.hide(); + $( 'select.attribute_taxonomy' ) + .find( + 'option[value="' + $parent.data( 'taxonomy' ) + '"]' + ) + .prop( 'disabled', false ); + selectedAttributes = selectedAttributes.filter( + ( attr ) => attr !== $parent.data( 'taxonomy' ) + ); + $( 'select.wc-attribute-search' ).data( + 'disabled-items', + selectedAttributes + ); + } else { + $parent.find( 'select, input[type=text]' ).val( '' ); + $parent.hide(); + attribute_row_indexes(); + } + + $parent.remove(); + + if ( + ! $( '.woocommerce_attribute_data' ).is( ':visible' ) && + ! $( 'div.add-global-attribute-container' ).hasClass( + 'hidden' + ) && + $( '.product_attributes' ).find( 'input, select, textarea' ) + .length === 0 + ) { + toggle_add_global_attribute_layout(); + } + jQuery.maybe_disable_save_button(); } - - $parent.remove(); - - if ( - ! $( '.woocommerce_attribute_data' ).is( ':visible' ) && - ! $( 'div.add-global-attribute-container' ).hasClass( - 'hidden' - ) && - $( '.product_attributes' ).find( 'input, select, textarea' ) - .length === 0 - ) { - toggle_add_global_attribute_layout(); - } - jQuery.maybe_disable_save_button(); + return false; } - return false; - } ); + ); // Attribute ordering. $( '.product_attributes' ).sortable( { diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-assets.php b/plugins/woocommerce/includes/admin/class-wc-admin-assets.php index 4c2ba397706..04e9a4b1334 100644 --- a/plugins/woocommerce/includes/admin/class-wc-admin-assets.php +++ b/plugins/woocommerce/includes/admin/class-wc-admin-assets.php @@ -432,6 +432,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) : 'i18n_save_attribute_variation_tip' => __( 'Make sure you enter the name and values for each attribute.', 'woocommerce' ), /* translators: %1$s: maximum file size */ 'i18n_product_image_tip' => sprintf( __( 'For best results, upload JPEG or PNG files that are 1000 by 1000 pixels or larger. Maximum upload file size: %1$s.', 'woocommerce' ) , size_format( wp_max_upload_size() ) ), + 'i18n_remove_used_attribute_confirmation_message' => __( 'If you remove this attribute, customers will no longer be able to purchase some variations of this product.', 'woocommerce' ), ); wp_localize_script( 'wc-admin-meta-boxes', 'woocommerce_admin_meta_boxes', $params ); From 4cc6644c8badfa07436016bb5ff00aff10225370 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Tue, 11 Apr 2023 14:13:59 +0800 Subject: [PATCH 170/466] Add default priority for countries that don't exist in payment recommendation map (#37590) * Add default priority for countries that are not in the payment recommendation map * Add changelog * Add doc * Fix linting --- .../update-default-payment-gateway-priority | 4 ++ .../DefaultPaymentGateways.php | 50 ++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 plugins/woocommerce/changelog/update-default-payment-gateway-priority diff --git a/plugins/woocommerce/changelog/update-default-payment-gateway-priority b/plugins/woocommerce/changelog/update-default-payment-gateway-priority new file mode 100644 index 00000000000..1313a944650 --- /dev/null +++ b/plugins/woocommerce/changelog/update-default-payment-gateway-priority @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Add default priority for countries that are not in the payment recommendation map diff --git a/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/DefaultPaymentGateways.php b/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/DefaultPaymentGateways.php index fbe2de7ab01..9f96721ec96 100644 --- a/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/DefaultPaymentGateways.php +++ b/plugins/woocommerce/src/Admin/Features/PaymentGatewaySuggestions/DefaultPaymentGateways.php @@ -11,6 +11,38 @@ defined( 'ABSPATH' ) || exit; * Default Payment Gateways */ class DefaultPaymentGateways { + /** + * This is the default priority for countries that are not in the $recommendation_priority_map. + * Priority is used to determine which payment gateway to recommend first. + * The lower the number, the higher the priority. + * + * @var array + */ + private static $recommendation_priority = array( + 'woocommerce_payments' => 1, + 'woocommerce_payments:with-in-person-payments' => 1, + 'woocommerce_payments:without-in-person-payments' => 1, + 'stripe' => 2, + 'woo-mercado-pago-custom' => 3, + // PayPal Payments. + 'ppcp-gateway' => 4, + 'mollie_wc_gateway_banktransfer' => 5, + 'razorpay' => 5, + 'payfast' => 5, + 'payubiz' => 6, + 'square_credit_card' => 6, + 'klarna_payments' => 6, + // Klarna Checkout. + 'kco' => 6, + 'paystack' => 6, + 'eway' => 7, + 'amazon_payments_advanced' => 7, + 'affirm' => 8, + 'afterpay' => 9, + 'zipmoney' => 10, + 'payoneer-checkout' => 11, + ); + /** * Get default specs. * @@ -1126,9 +1158,9 @@ class DefaultPaymentGateways { 'GH' => [ 'paystack', 'ppcp-gateway' ], ); - // If the country code is not in the list, return null. + // If the country code is not in the list, return default priority. if ( ! isset( $recommendation_priority_map[ $country_code ] ) ) { - return null; + return self::get_default_recommendation_priority( $gateway_id ); } $index = array_search( $gateway_id, $recommendation_priority_map[ $country_code ], true ); @@ -1140,4 +1172,18 @@ class DefaultPaymentGateways { return $index; } + + /** + * Get the default recommendation priority for a payment gateway. + * This is used when a country is not in the $recommendation_priority_map array. + * + * @param string $id Payment gateway id. + * @return int Priority. + */ + private static function get_default_recommendation_priority( $id ) { + if ( ! $id || ! array_key_exists( $id, self::$recommendation_priority ) ) { + return null; + } + return self::$recommendation_priority[ $id ]; + } } From 50593c5257d4e7036280201d01cdf73469927706 Mon Sep 17 00:00:00 2001 From: Darren Ethier Date: Tue, 11 Apr 2023 03:27:38 -0400 Subject: [PATCH 171/466] Fix issue that breaks the WooCommerce Home Page when Gutenberg 15.5 is active (#37641) Co-authored-by: Ilyas Foo Co-authored-by: Chi-Hsuan Huang Co-authored-by: rjchow --- .../changelog/fix-gutengerg15-5-update-issues | 4 + .../onboarding/with-onboarding-hydration.tsx | 78 +++++++++++-------- packages/js/data/src/settings/index.ts | 4 +- .../src/settings/with-settings-hydration.tsx | 53 ++++++++----- .../client/homescreen/index.tsx | 9 +-- .../client/homescreen/layout.js | 7 -- .../changelog/fix-gutengerg15-5-update-issues | 4 + 7 files changed, 93 insertions(+), 66 deletions(-) create mode 100644 packages/js/data/changelog/fix-gutengerg15-5-update-issues create mode 100644 plugins/woocommerce/changelog/fix-gutengerg15-5-update-issues diff --git a/packages/js/data/changelog/fix-gutengerg15-5-update-issues b/packages/js/data/changelog/fix-gutengerg15-5-update-issues new file mode 100644 index 00000000000..5e9a1834400 --- /dev/null +++ b/packages/js/data/changelog/fix-gutengerg15-5-update-issues @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix incorrect usage of dispatch, useSelect, and setState calls in homescreen along with settings and onboarding package diff --git a/packages/js/data/src/onboarding/with-onboarding-hydration.tsx b/packages/js/data/src/onboarding/with-onboarding-hydration.tsx index 91732717c7e..e29595b5815 100644 --- a/packages/js/data/src/onboarding/with-onboarding-hydration.tsx +++ b/packages/js/data/src/onboarding/with-onboarding-hydration.tsx @@ -2,15 +2,14 @@ * External dependencies */ import { createHigherOrderComponent } from '@wordpress/compose'; -import { useSelect } from '@wordpress/data'; -import { createElement, useRef } from '@wordpress/element'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { createElement, useEffect, useRef } from '@wordpress/element'; /** * Internal dependencies */ import { STORE_NAME } from './constants'; import { ProfileItems } from './types'; -import { OnboardingSelector } from './'; export const withOnboardingHydration = ( data: { profileItems: ProfileItems; @@ -21,39 +20,54 @@ export const withOnboardingHydration = ( data: { ( OriginalComponent ) => ( props ) => { const onboardingRef = useRef( data ); - useSelect( - // @ts-expect-error // @ts-expect-error registry is not defined in the wp.data typings - ( select: ( s: string ) => OnboardingSelector, registry ) => { - if ( ! onboardingRef.current ) { - return; - } - + const { isResolvingGroup, hasFinishedResolutionGroup } = useSelect( + ( select ) => { const { isResolving, hasFinishedResolution } = select( STORE_NAME ); - const { - startResolution, - finishResolution, - setProfileItems, - } = registry.dispatch( STORE_NAME ); - - const { profileItems } = onboardingRef.current; - - if ( - profileItems && - ! hydratedProfileItems && - ! isResolving( 'getProfileItems', [] ) && - ! hasFinishedResolution( 'getProfileItems', [] ) - ) { - startResolution( 'getProfileItems', [] ); - setProfileItems( profileItems, true ); - finishResolution( 'getProfileItems', [] ); - - hydratedProfileItems = true; - } - }, - [] + return { + isResolvingGroup: isResolving( 'getProfileItems', [] ), + hasFinishedResolutionGroup: hasFinishedResolution( + 'getProfileItems', + [] + ), + }; + } ); + const { startResolution, finishResolution, setProfileItems } = + useDispatch( STORE_NAME ); + + useEffect( () => { + if ( ! onboardingRef.current ) { + return; + } + + const { profileItems } = onboardingRef.current; + if ( ! profileItems ) { + return; + } + + if ( + profileItems && + ! hydratedProfileItems && + // Ensure that profile items have finished resolving to prevent race conditions + ! isResolvingGroup && + ! hasFinishedResolutionGroup + ) { + startResolution( 'getProfileItems', [] ); + setProfileItems( profileItems, true ); + finishResolution( 'getProfileItems', [] ); + + hydratedProfileItems = true; + } + }, [ + finishResolution, + setProfileItems, + startResolution, + isResolvingGroup, + hasFinishedResolutionGroup, + ] ); + return ; }, 'withOnboardingHydration' diff --git a/packages/js/data/src/settings/index.ts b/packages/js/data/src/settings/index.ts index f098a3c82eb..cd21446c45c 100644 --- a/packages/js/data/src/settings/index.ts +++ b/packages/js/data/src/settings/index.ts @@ -10,7 +10,7 @@ import { SelectFromMap, DispatchFromMap } from '@automattic/data-stores'; /** * Internal dependencies */ -import { WPDataSelectors } from '../types'; +import { WPDataSelectors, WPDataActions } from '../types'; import { STORE_NAME } from './constants'; import * as selectors from './selectors'; import * as actions from './actions'; @@ -33,7 +33,7 @@ export const SETTINGS_STORE_NAME = STORE_NAME; declare module '@wordpress/data' { function dispatch( key: typeof STORE_NAME - ): DispatchFromMap< typeof actions >; + ): DispatchFromMap< typeof actions > & WPDataActions; function select( key: typeof STORE_NAME ): SelectFromMap< typeof selectors > & WPDataSelectors; diff --git a/packages/js/data/src/settings/with-settings-hydration.tsx b/packages/js/data/src/settings/with-settings-hydration.tsx index 02cb0a49933..6d09addc568 100644 --- a/packages/js/data/src/settings/with-settings-hydration.tsx +++ b/packages/js/data/src/settings/with-settings-hydration.tsx @@ -2,8 +2,8 @@ * External dependencies */ import { createHigherOrderComponent } from '@wordpress/compose'; -import { useSelect, select as wpSelect } from '@wordpress/data'; -import { createElement, useRef } from '@wordpress/element'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { createElement, useRef, useEffect } from '@wordpress/element'; /** * Internal dependencies @@ -16,31 +16,46 @@ export const withSettingsHydration = ( group: string, settings: Settings ) => ( OriginalComponent ) => ( props ) => { const settingsRef = useRef( settings ); - // @ts-expect-error registry is not defined in the wp.data typings - useSelect( ( select: typeof wpSelect, registry ) => { + const { + startResolution, + finishResolution, + updateSettingsForGroup, + clearIsDirty, + } = useDispatch( STORE_NAME ); + const { isResolvingGroup, hasFinishedResolutionGroup } = useSelect( + ( select ) => { + const { isResolving, hasFinishedResolution } = + select( STORE_NAME ); + return { + isResolvingGroup: isResolving( 'getSettings', [ + group, + ] ), + hasFinishedResolutionGroup: hasFinishedResolution( + 'getSettings', + [ group ] + ), + }; + }, + [] + ); + useEffect( () => { if ( ! settingsRef.current ) { return; } - - const { isResolving, hasFinishedResolution } = - select( STORE_NAME ); - const { - startResolution, - finishResolution, - updateSettingsForGroup, - clearIsDirty, - } = registry.dispatch( STORE_NAME ); - - if ( - ! isResolving( 'getSettings', [ group ] ) && - ! hasFinishedResolution( 'getSettings', [ group ] ) - ) { + if ( ! isResolvingGroup && ! hasFinishedResolutionGroup ) { startResolution( 'getSettings', [ group ] ); updateSettingsForGroup( group, settingsRef.current ); clearIsDirty( group ); finishResolution( 'getSettings', [ group ] ); } - }, [] ); + }, [ + isResolvingGroup, + hasFinishedResolutionGroup, + finishResolution, + updateSettingsForGroup, + startResolution, + clearIsDirty, + ] ); return ; }, diff --git a/plugins/woocommerce-admin/client/homescreen/index.tsx b/plugins/woocommerce-admin/client/homescreen/index.tsx index e0199f9cddf..a9d80c505d4 100644 --- a/plugins/woocommerce-admin/client/homescreen/index.tsx +++ b/plugins/woocommerce-admin/client/homescreen/index.tsx @@ -3,7 +3,6 @@ */ import { compose } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; -import { identity } from 'lodash'; import { ONBOARDING_STORE_NAME, withOnboardingHydration, @@ -50,10 +49,8 @@ const withSelectHandler = ( select: WCDataSelector ) => { }; export default compose( - onboardingData.profile - ? withOnboardingHydration( { - profileItems: onboardingData.profile, - } ) - : identity, + withOnboardingHydration( { + profileItems: onboardingData.profile, + } ), withSelect( withSelectHandler ) )( Homescreen ); diff --git a/plugins/woocommerce-admin/client/homescreen/layout.js b/plugins/woocommerce-admin/client/homescreen/layout.js index 6fc310ebdcd..025c79e9fab 100644 --- a/plugins/woocommerce-admin/client/homescreen/layout.js +++ b/plugins/woocommerce-admin/client/homescreen/layout.js @@ -7,7 +7,6 @@ import { useCallback, useLayoutEffect, useRef, - useState, } from '@wordpress/element'; import { compose } from '@wordpress/compose'; import { withDispatch, withSelect } from '@wordpress/data'; @@ -53,7 +52,6 @@ const Tasks = lazy( () => export const Layout = ( { defaultHomescreenLayout, - isBatchUpdating, query, taskListComplete, hasTaskList, @@ -68,7 +66,6 @@ export const Layout = ( { const shouldShowStoreLinks = taskListComplete || isTaskListHidden; const hasTwoColumnContent = shouldShowStoreLinks || window.wcAdminFeatures.analytics; - const [ showInbox, setShowInbox ] = useState( true ); const isDashboardShown = ! query.task; // ?&task= query param is used to show tasks instead of the homescreen const activeSetupTaskList = useActiveSetupTasklist(); @@ -76,10 +73,6 @@ export const Layout = ( { ( userPrefs.homepage_layout || defaultHomescreenLayout ) === 'two_columns' && hasTwoColumnContent; - if ( isBatchUpdating && ! showInbox ) { - setShowInbox( true ); - } - const isWideViewport = useRef( true ); const maybeToggleColumns = useCallback( () => { isWideViewport.current = window.innerWidth >= 782; diff --git a/plugins/woocommerce/changelog/fix-gutengerg15-5-update-issues b/plugins/woocommerce/changelog/fix-gutengerg15-5-update-issues new file mode 100644 index 00000000000..5e9a1834400 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-gutengerg15-5-update-issues @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix incorrect usage of dispatch, useSelect, and setState calls in homescreen along with settings and onboarding package From fa4dd9040125ee83bdc3d4e3e4f14be4f4239499 Mon Sep 17 00:00:00 2001 From: Nathan Silveira Date: Tue, 11 Apr 2023 11:44:27 -0300 Subject: [PATCH 172/466] Change link all variations label (#37632) --- .../src/specs/merchant/wp-admin-product-new.test.js | 2 +- plugins/woocommerce/changelog/tweak-variations-label | 5 +++++ plugins/woocommerce/includes/admin/class-wc-admin-assets.php | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 plugins/woocommerce/changelog/tweak-variations-label diff --git a/packages/js/e2e-core-tests/src/specs/merchant/wp-admin-product-new.test.js b/packages/js/e2e-core-tests/src/specs/merchant/wp-admin-product-new.test.js index 30029f9a06d..c631e52cd97 100644 --- a/packages/js/e2e-core-tests/src/specs/merchant/wp-admin-product-new.test.js +++ b/packages/js/e2e-core-tests/src/specs/merchant/wp-admin-product-new.test.js @@ -222,7 +222,7 @@ const runAddVariableProductTest = () => { ); await expect( firstDialog.message() ).toMatch( - 'Are you sure you want to link all variations?' + 'Do you want to generate all variations?' ); // Set some variation data diff --git a/plugins/woocommerce/changelog/tweak-variations-label b/plugins/woocommerce/changelog/tweak-variations-label new file mode 100644 index 00000000000..bcb141646af --- /dev/null +++ b/plugins/woocommerce/changelog/tweak-variations-label @@ -0,0 +1,5 @@ +Significance: patch +Type: tweak +Comment: Small label change + + diff --git a/plugins/woocommerce/includes/admin/class-wc-admin-assets.php b/plugins/woocommerce/includes/admin/class-wc-admin-assets.php index 04e9a4b1334..1407dac5a2e 100644 --- a/plugins/woocommerce/includes/admin/class-wc-admin-assets.php +++ b/plugins/woocommerce/includes/admin/class-wc-admin-assets.php @@ -287,7 +287,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) : 'save_variations_nonce' => wp_create_nonce( 'save-variations' ), 'bulk_edit_variations_nonce' => wp_create_nonce( 'bulk-edit-variations' ), /* translators: %d: Number of variations */ - 'i18n_link_all_variations' => esc_js( sprintf( __( 'Are you sure you want to link all variations? This will create a new variation for each and every possible combination of variation attributes (max %d per run).', 'woocommerce' ), Constants::is_defined( 'WC_MAX_LINKED_VARIATIONS' ) ? Constants::get_constant( 'WC_MAX_LINKED_VARIATIONS' ) : 50 ) ), + 'i18n_link_all_variations' => esc_js( sprintf( __( 'Do you want to generate all variations? This will create a new variation for each and every possible combination of variation attributes (max %d per run).', 'woocommerce' ), Constants::is_defined( 'WC_MAX_LINKED_VARIATIONS' ) ? Constants::get_constant( 'WC_MAX_LINKED_VARIATIONS' ) : 50 ) ), 'i18n_enter_a_value' => esc_js( __( 'Enter a value', 'woocommerce' ) ), 'i18n_enter_menu_order' => esc_js( __( 'Variation menu order (determines position in the list of variations)', 'woocommerce' ) ), 'i18n_enter_a_value_fixed_or_percent' => esc_js( __( 'Enter a value (fixed or %)', 'woocommerce' ) ), From 8354ae279215ad7020ef841707625f949f4419f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maikel=20David=20P=C3=A9rez=20G=C3=B3mez?= Date: Tue, 11 Apr 2023 10:53:37 -0400 Subject: [PATCH 173/466] Create schedule sale pricing block (#37567) * Create schedule sale pricing block * Add auto show/hide and clean fields depending on the initial values * Add validations to From and To dates fields * Add changelog files * Align block with the figma design * Revert linter replacement in the editor global style.scss file * Fix some words typos * Move and rename schedule sale block to the blocks folder * Load datetime format from @wordpress/date instead of using the woo options datastore * Remove the min difference using by moment to compare dates from 'minute' to undefined * Make sure the dates are normalized for comparison --- .../js/product-editor/changelog/add-37394 | 4 + packages/js/product-editor/package.json | 3 + .../src/blocks/schedule-sale/block.json | 25 + .../src/blocks/schedule-sale/edit.tsx | 151 ++++++ .../src/blocks/schedule-sale/editor.scss | 16 + .../src/blocks/schedule-sale/index.ts | 29 ++ .../src/blocks/schedule-sale/types.ts | 8 + .../src/components/editor/init-blocks.ts | 2 + packages/js/product-editor/src/constants.ts | 2 + packages/js/product-editor/src/style.scss | 1 + plugins/woocommerce/changelog/add-37394 | 4 + .../includes/class-wc-post-types.php | 3 + pnpm-lock.yaml | 438 ++++++++++-------- 13 files changed, 480 insertions(+), 206 deletions(-) create mode 100644 packages/js/product-editor/changelog/add-37394 create mode 100644 packages/js/product-editor/src/blocks/schedule-sale/block.json create mode 100644 packages/js/product-editor/src/blocks/schedule-sale/edit.tsx create mode 100644 packages/js/product-editor/src/blocks/schedule-sale/editor.scss create mode 100644 packages/js/product-editor/src/blocks/schedule-sale/index.ts create mode 100644 packages/js/product-editor/src/blocks/schedule-sale/types.ts create mode 100644 plugins/woocommerce/changelog/add-37394 diff --git a/packages/js/product-editor/changelog/add-37394 b/packages/js/product-editor/changelog/add-37394 new file mode 100644 index 00000000000..f1628ce5381 --- /dev/null +++ b/packages/js/product-editor/changelog/add-37394 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add product schedule sale pricing block diff --git a/packages/js/product-editor/package.json b/packages/js/product-editor/package.json index a5e2de6b022..f508c8d9d77 100644 --- a/packages/js/product-editor/package.json +++ b/packages/js/product-editor/package.json @@ -47,6 +47,7 @@ "@wordpress/compose": "wp-6.0", "@wordpress/core-data": "wp-6.0", "@wordpress/data": "wp-6.0", + "@wordpress/date": "wp-6.0", "@wordpress/editor": "wp-6.0", "@wordpress/element": "wp-6.0", "@wordpress/html-entities": "wp-6.0", @@ -60,6 +61,7 @@ "classnames": "^2.3.1", "dompurify": "^2.3.6", "lodash": "^4.17.21", + "moment": "^2.29.4", "prop-types": "^15.8.1", "react-router-dom": "^6.3.0" }, @@ -80,6 +82,7 @@ "@types/wordpress__components": "^19.10.3", "@types/wordpress__core-data": "^2.4.5", "@types/wordpress__data": "^6.0.2", + "@types/wordpress__date": "^3.3.2", "@types/wordpress__editor": "^13.0.0", "@types/wordpress__media-utils": "^3.0.0", "@types/wordpress__plugins": "^3.0.0", diff --git a/packages/js/product-editor/src/blocks/schedule-sale/block.json b/packages/js/product-editor/src/blocks/schedule-sale/block.json new file mode 100644 index 00000000000..73a0afb04a9 --- /dev/null +++ b/packages/js/product-editor/src/blocks/schedule-sale/block.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "woocommerce/product-schedule-sale-fields", + "title": "Product schedule sale fields", + "category": "woocommerce", + "description": "The product schedule sale fields.", + "keywords": [ "products", "schedule", "sale" ], + "textdomain": "default", + "attributes": { + "name": { + "type": "string", + "__experimentalRole": "content" + } + }, + "supports": { + "align": false, + "html": false, + "multiple": true, + "reusable": false, + "inserter": false, + "lock": false + }, + "editorStyle": "file:./editor.css" +} diff --git a/packages/js/product-editor/src/blocks/schedule-sale/edit.tsx b/packages/js/product-editor/src/blocks/schedule-sale/edit.tsx new file mode 100644 index 00000000000..c54916be426 --- /dev/null +++ b/packages/js/product-editor/src/blocks/schedule-sale/edit.tsx @@ -0,0 +1,151 @@ +/** + * External dependencies + */ +import { DateTimePickerControl } from '@woocommerce/components'; +import { recordEvent } from '@woocommerce/tracks'; +import { useBlockProps } from '@wordpress/block-editor'; +import { BlockEditProps } from '@wordpress/blocks'; +import { ToggleControl } from '@wordpress/components'; +import { useEntityProp } from '@wordpress/core-data'; +import { createElement, useEffect, useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import moment from 'moment'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore We need this to get the datetime format for the DateTimePickerControl. +// eslint-disable-next-line @woocommerce/dependency-group +import { getSettings } from '@wordpress/date'; + +/** + * Internal dependencies + */ +import { ScheduleSalePricingBlockAttributes } from './types'; +import { useValidation } from '../../hooks/use-validation'; + +export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > ) { + const blockProps = useBlockProps( { + className: 'wp-block-woocommerce-product-schedule-sale-fields', + } ); + + const dateTimeFormat = getSettings().formats.datetime; + + const [ showScheduleSale, setShowScheduleSale ] = useState( false ); + + const [ salePrice ] = useEntityProp< string | null >( + 'postType', + 'product', + 'sale_price' + ); + + const isSalePriceGreaterThanZero = + Number.parseFloat( salePrice || '0' ) > 0; + + const [ dateOnSaleFromGmt, setDateOnSaleFromGmt ] = useEntityProp< + string | null + >( 'postType', 'product', 'date_on_sale_from_gmt' ); + + const [ dateOnSaleToGmt, setDateOnSaleToGmt ] = useEntityProp< + string | null + >( 'postType', 'product', 'date_on_sale_to_gmt' ); + + const today = moment().startOf( 'minute' ).toISOString(); + + function handleToggleChange( value: boolean ) { + recordEvent( 'product_pricing_schedule_sale_toggle_click', { + enabled: value, + } ); + + setShowScheduleSale( value ); + + if ( value ) { + setDateOnSaleFromGmt( today ); + setDateOnSaleToGmt( '' ); + } else { + setDateOnSaleFromGmt( '' ); + setDateOnSaleToGmt( '' ); + } + } + + // Hide and clean date fields if the user manually change + // the sale price to zero or less. + useEffect( () => { + if ( ! isSalePriceGreaterThanZero ) { + setShowScheduleSale( false ); + setDateOnSaleFromGmt( '' ); + setDateOnSaleToGmt( '' ); + } + }, [ isSalePriceGreaterThanZero ] ); + + // Automatically show date fields if `from` or `to` dates have + // any value. + useEffect( () => { + if ( dateOnSaleFromGmt || dateOnSaleToGmt ) { + setShowScheduleSale( true ); + } + }, [ dateOnSaleFromGmt, dateOnSaleToGmt ] ); + + const isDateOnSaleToGmtValid = useValidation( + 'product/date_on_sale_to_gmt', + () => + ! showScheduleSale || + ! dateOnSaleToGmt || + moment( dateOnSaleFromGmt ).isBefore( dateOnSaleToGmt ) + ); + + return ( +
    + + + { showScheduleSale && ( +
    +
    + +
    + +
    + + setDateOnSaleToGmt( + moment( value ) + .startOf( 'minute' ) + .toISOString() + ) + } + className={ + isDateOnSaleToGmtValid ? undefined : 'has-error' + } + help={ + isDateOnSaleToGmtValid + ? undefined + : __( + 'To date must be greater than From date.', + 'woocommerce' + ) + } + /> +
    +
    + ) } +
    + ); +} diff --git a/packages/js/product-editor/src/blocks/schedule-sale/editor.scss b/packages/js/product-editor/src/blocks/schedule-sale/editor.scss new file mode 100644 index 00000000000..5abe6c88e80 --- /dev/null +++ b/packages/js/product-editor/src/blocks/schedule-sale/editor.scss @@ -0,0 +1,16 @@ +.wp-block-woocommerce-product-schedule-sale-fields { + .components-toggle-control__label { + display: flex; + align-items: center; + } + + .woocommerce-date-time-picker-control.has-error { + .components-base-control .components-input-control__backdrop { + border-color: $studio-red-50; + } + + .components-base-control__help { + color: $studio-red-50; + } + } +} diff --git a/packages/js/product-editor/src/blocks/schedule-sale/index.ts b/packages/js/product-editor/src/blocks/schedule-sale/index.ts new file mode 100644 index 00000000000..e3893b6f2ac --- /dev/null +++ b/packages/js/product-editor/src/blocks/schedule-sale/index.ts @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { BlockConfiguration } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { initBlock } from '../../utils/init-blocks'; +import blockConfiguration from './block.json'; +import { Edit } from './edit'; +import { ScheduleSalePricingBlockAttributes } from './types'; + +const { name, ...metadata } = + blockConfiguration as BlockConfiguration< ScheduleSalePricingBlockAttributes >; + +export { metadata, name }; + +export const settings: Partial< + BlockConfiguration< ScheduleSalePricingBlockAttributes > +> = { + example: {}, + edit: Edit, +}; + +export function init() { + return initBlock( { name, metadata, settings } ); +} diff --git a/packages/js/product-editor/src/blocks/schedule-sale/types.ts b/packages/js/product-editor/src/blocks/schedule-sale/types.ts new file mode 100644 index 00000000000..e8c038493c0 --- /dev/null +++ b/packages/js/product-editor/src/blocks/schedule-sale/types.ts @@ -0,0 +1,8 @@ +/** + * External dependencies + */ +import { BlockAttributes } from '@wordpress/blocks'; + +export interface ScheduleSalePricingBlockAttributes extends BlockAttributes { + name: string; +} diff --git a/packages/js/product-editor/src/components/editor/init-blocks.ts b/packages/js/product-editor/src/components/editor/init-blocks.ts index 7555c6a2a1f..c81c2aa288a 100644 --- a/packages/js/product-editor/src/components/editor/init-blocks.ts +++ b/packages/js/product-editor/src/components/editor/init-blocks.ts @@ -20,6 +20,7 @@ import { init as initSection } from '../section'; import { init as initTab } from '../tab'; import { init as initPricing } from '../pricing-block'; import { init as initCollapsible } from '../collapsible-block'; +import { init as initScheduleSale } from '../../blocks/schedule-sale'; export const initBlocks = () => { const coreBlocks = __experimentalGetCoreBlocks(); @@ -38,4 +39,5 @@ export const initBlocks = () => { initTab(); initPricing(); initCollapsible(); + initScheduleSale(); }; diff --git a/packages/js/product-editor/src/constants.ts b/packages/js/product-editor/src/constants.ts index 8d94a1fc8e4..3afadee6a1a 100644 --- a/packages/js/product-editor/src/constants.ts +++ b/packages/js/product-editor/src/constants.ts @@ -47,3 +47,5 @@ export const VARIANT_SHIPPING_SECTION_BASIC_ID = `variant/${ SHIPPING_SECTION_BA export const VARIANT_SHIPPING_SECTION_DIMENSIONS_ID = `variant/${ SHIPPING_SECTION_DIMENSIONS_ID }`; export const PRODUCT_DETAILS_SLUG = 'product-details'; + +export const PRODUCT_SCHEDULED_SALE_SLUG = 'product-scheduled-sale'; diff --git a/packages/js/product-editor/src/style.scss b/packages/js/product-editor/src/style.scss index 0287d7c8690..7e2107e3fdb 100644 --- a/packages/js/product-editor/src/style.scss +++ b/packages/js/product-editor/src/style.scss @@ -1,3 +1,4 @@ +@import 'blocks/schedule-sale/editor.scss'; @import 'components/editor/style.scss'; @import 'components/product-section-layout/style.scss'; @import 'components/edit-product-link-modal/style.scss'; diff --git a/plugins/woocommerce/changelog/add-37394 b/plugins/woocommerce/changelog/add-37394 new file mode 100644 index 00000000000..cd427860cb2 --- /dev/null +++ b/plugins/woocommerce/changelog/add-37394 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add product schedule sale pricing block to blocks template definition diff --git a/plugins/woocommerce/includes/class-wc-post-types.php b/plugins/woocommerce/includes/class-wc-post-types.php index d2a8a6083e9..19b5093c99e 100644 --- a/plugins/woocommerce/includes/class-wc-post-types.php +++ b/plugins/woocommerce/includes/class-wc-post-types.php @@ -513,6 +513,9 @@ class WC_Post_Types { ), ), ), + array( + 'woocommerce/product-schedule-sale-fields', + ), array( 'woocommerce/product-radio', array( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c258c37ed61..26415e7da7c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1152,7 +1152,7 @@ importers: typescript: ^4.9.5 dependencies: '@testing-library/jest-dom': 5.16.2 - '@testing-library/react': 12.1.4_sfoxds7t5ydpegc3knd667wn6m + '@testing-library/react': 12.1.4_6l5554ty5ajsajah6yazvrjhoe '@wordpress/data': 6.6.1_react@17.0.2 '@wordpress/i18n': 4.6.1 '@wordpress/jest-console': 5.0.2_jest@27.5.1 @@ -1424,6 +1424,7 @@ importers: '@types/wordpress__components': ^19.10.3 '@types/wordpress__core-data': ^2.4.5 '@types/wordpress__data': ^6.0.2 + '@types/wordpress__date': ^3.3.2 '@types/wordpress__editor': ^13.0.0 '@types/wordpress__media-utils': ^3.0.0 '@types/wordpress__plugins': ^3.0.0 @@ -1447,6 +1448,7 @@ importers: '@wordpress/compose': wp-6.0 '@wordpress/core-data': wp-6.0 '@wordpress/data': wp-6.0 + '@wordpress/date': wp-6.0 '@wordpress/editor': wp-6.0 '@wordpress/element': wp-6.0 '@wordpress/html-entities': wp-6.0 @@ -1466,6 +1468,7 @@ importers: jest: ^27.5.1 jest-cli: ^27.5.1 lodash: ^4.17.21 + moment: ^2.29.4 postcss: ^8.4.7 postcss-loader: ^4.3.0 prop-types: ^15.8.1 @@ -1499,6 +1502,7 @@ importers: '@wordpress/compose': 5.4.1_react@17.0.2 '@wordpress/core-data': 4.4.5_react@17.0.2 '@wordpress/data': 6.6.1_react@17.0.2 + '@wordpress/date': 4.6.1 '@wordpress/editor': 12.5.10_eqi5qhcxfphl6j3pngzexvnehi '@wordpress/element': 4.4.1 '@wordpress/html-entities': 3.6.1 @@ -1512,6 +1516,7 @@ importers: classnames: 2.3.1 dompurify: 2.3.6 lodash: 4.17.21 + moment: 2.29.4 prop-types: 15.8.1 react-router-dom: 6.3.0_sfoxds7t5ydpegc3knd667wn6m devDependencies: @@ -1530,6 +1535,7 @@ importers: '@types/wordpress__components': 19.10.5_sfoxds7t5ydpegc3knd667wn6m '@types/wordpress__core-data': 2.4.5 '@types/wordpress__data': 6.0.2 + '@types/wordpress__date': 3.3.2 '@types/wordpress__editor': 13.0.0_sfoxds7t5ydpegc3knd667wn6m '@types/wordpress__media-utils': 3.0.0_sfoxds7t5ydpegc3knd667wn6m '@types/wordpress__plugins': 3.0.0_sfoxds7t5ydpegc3knd667wn6m @@ -3084,9 +3090,9 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.21.0 - '@babel/helper-member-expression-to-functions': 7.21.0 + '@babel/helper-member-expression-to-functions': 7.18.9 '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-replace-supers': 7.19.1 '@babel/helper-split-export-declaration': 7.18.6 transitivePeerDependencies: - supports-color @@ -3101,9 +3107,9 @@ packages: '@babel/helper-annotate-as-pure': 7.18.6 '@babel/helper-environment-visitor': 7.18.9 '@babel/helper-function-name': 7.21.0 - '@babel/helper-member-expression-to-functions': 7.21.0 + '@babel/helper-member-expression-to-functions': 7.18.9 '@babel/helper-optimise-call-expression': 7.18.6 - '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-replace-supers': 7.19.1 '@babel/helper-split-export-declaration': 7.18.6 transitivePeerDependencies: - supports-color @@ -5102,7 +5108,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.17.8 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.19.0 dev: true /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.21.3: @@ -5111,7 +5117,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.21.3 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.19.0 /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.12.9: resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} @@ -6975,8 +6981,8 @@ packages: dependencies: '@babel/core': 7.21.3 '@babel/helper-module-imports': 7.16.0 - '@babel/helper-plugin-utils': 7.14.5 - babel-plugin-polyfill-corejs2: 0.3.0_@babel+core@7.21.3 + '@babel/helper-plugin-utils': 7.20.2 + babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.21.3 babel-plugin-polyfill-corejs3: 0.4.0_@babel+core@7.21.3 babel-plugin-polyfill-regenerator: 0.3.0_@babel+core@7.21.3 semver: 6.3.0 @@ -14034,6 +14040,20 @@ packages: react-error-boundary: 3.1.4_react@17.0.2 dev: true + /@testing-library/react/12.1.4_6l5554ty5ajsajah6yazvrjhoe: + resolution: {integrity: sha512-jiPKOm7vyUw311Hn/HlNQ9P8/lHNtArAx0PisXyFixDDvfl8DbD6EUdbshK5eqauvBSvzZd19itqQ9j3nferJA==} + engines: {node: '>=12'} + peerDependencies: + react: '*' + react-dom: '*' + dependencies: + '@babel/runtime': 7.21.0 + '@testing-library/dom': 8.11.3 + '@types/react-dom': 17.0.17 + react: 17.0.2 + react-dom: 18.2.0_react@17.0.2 + dev: false + /@testing-library/react/12.1.4_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-jiPKOm7vyUw311Hn/HlNQ9P8/lHNtArAx0PisXyFixDDvfl8DbD6EUdbshK5eqauvBSvzZd19itqQ9j3nferJA==} engines: {node: '>=12'} @@ -14046,6 +14066,7 @@ packages: '@types/react-dom': 17.0.17 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 + dev: true /@testing-library/user-event/13.5.0_gzufz4q333be4gqfrvipwvqt6a: resolution: {integrity: sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==} @@ -14666,7 +14687,7 @@ packages: '@types/wordpress__components': 19.10.5_sfoxds7t5ydpegc3knd667wn6m '@types/wordpress__data': 6.0.2 '@types/wordpress__keycodes': 2.3.1 - '@wordpress/element': 4.4.1 + '@wordpress/element': 4.20.0 react-autosize-textarea: 7.1.0_sfoxds7t5ydpegc3knd667wn6m transitivePeerDependencies: - react @@ -14680,7 +14701,7 @@ packages: dependencies: '@types/react': 17.0.50 '@types/wordpress__components': 19.10.5_sfoxds7t5ydpegc3knd667wn6m - '@wordpress/element': 4.4.1 + '@wordpress/element': 4.20.0 transitivePeerDependencies: - react - react-dom @@ -14692,7 +14713,7 @@ packages: '@types/tinycolor2': 1.4.3 '@types/wordpress__notices': 3.5.0 '@types/wordpress__rich-text': 3.4.6 - '@wordpress/element': 4.4.1 + '@wordpress/element': 4.20.0 downshift: 6.1.12_react@17.0.2 re-resizable: 6.9.5_prpqlkd37azqwypxturxi7uyci transitivePeerDependencies: @@ -14707,7 +14728,7 @@ packages: '@types/tinycolor2': 1.4.3 '@types/wordpress__notices': 3.5.0 '@types/wordpress__rich-text': 3.4.6 - '@wordpress/element': 4.4.1 + '@wordpress/element': 4.20.0 downshift: 6.1.12_react@17.0.2 re-resizable: 6.9.5_sfoxds7t5ydpegc3knd667wn6m transitivePeerDependencies: @@ -14746,6 +14767,12 @@ packages: '@types/react': 17.0.50 redux: 4.2.0 + /@types/wordpress__date/3.3.2: + resolution: {integrity: sha512-8ZW580Rn/NMVNC0uTHaPyWEMgtvymBDXybiJY6mufX5PdWGn4TGMik9ev3+nvwSXlOlz4Wsug/6jUwLfGJJH0w==} + dependencies: + moment: 2.29.4 + dev: true + /@types/wordpress__editor/13.0.0_sfoxds7t5ydpegc3knd667wn6m: resolution: {integrity: sha512-hqnO/L82pY90l5XX88nhJjGxHyKAC5Q2k68bQaUPIHduRi28jCoKrWw/s6Wa6DesFBealF/at6Df8ooQY0TZcQ==} dependencies: @@ -14770,7 +14797,7 @@ packages: dependencies: '@types/wordpress__block-editor': 7.0.0_sfoxds7t5ydpegc3knd667wn6m '@types/wordpress__core-data': 2.4.5 - '@wordpress/element': 4.4.1 + '@wordpress/element': 4.20.0 transitivePeerDependencies: - react - react-dom @@ -15559,8 +15586,8 @@ packages: engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - '@wordpress/dom-ready': 3.6.1 - '@wordpress/i18n': 4.6.1 + '@wordpress/dom-ready': 3.28.0 + '@wordpress/i18n': 4.28.0 dev: false /@wordpress/api-fetch/3.23.1_react-native@0.70.0: @@ -15588,8 +15615,8 @@ packages: engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - '@wordpress/i18n': 4.6.1 - '@wordpress/url': 3.7.1 + '@wordpress/i18n': 4.28.0 + '@wordpress/url': 3.29.0 dev: true /@wordpress/api-fetch/6.25.0: @@ -15605,8 +15632,8 @@ packages: engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - '@wordpress/i18n': 4.6.1 - '@wordpress/url': 3.7.1 + '@wordpress/i18n': 4.28.0 + '@wordpress/url': 3.29.0 dev: false /@wordpress/autop/3.19.0: @@ -15734,8 +15761,8 @@ packages: '@babel/runtime': 7.21.0 '@wordpress/babel-plugin-import-jsx-pragma': 3.2.0_@babel+core@7.21.3 '@wordpress/browserslist-config': 4.1.3 - '@wordpress/element': 4.4.1 - '@wordpress/warning': 2.6.1 + '@wordpress/element': 4.20.0 + '@wordpress/warning': 2.28.0 browserslist: 4.20.2 core-js: 3.29.1 transitivePeerDependencies: @@ -16327,20 +16354,20 @@ packages: '@emotion/styled': 11.8.1_c2qm47vaialpqni522adyu6za4 '@emotion/utils': 1.0.0 '@use-gesture/react': 10.2.10_react@17.0.2 - '@wordpress/a11y': 3.6.1 - '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/date': 4.6.1 - '@wordpress/deprecated': 3.6.1 - '@wordpress/dom': 3.6.1 - '@wordpress/element': 4.4.1 + '@wordpress/a11y': 3.28.0 + '@wordpress/compose': 5.17.0_react@17.0.2 + '@wordpress/date': 4.28.0 + '@wordpress/deprecated': 3.28.0 + '@wordpress/dom': 3.28.0 + '@wordpress/element': 4.20.0 '@wordpress/escape-html': 2.4.1 - '@wordpress/hooks': 3.6.1 - '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/hooks': 3.28.0 + '@wordpress/i18n': 4.28.0 + '@wordpress/icons': 8.4.0 '@wordpress/is-shallow-equal': 4.4.1 - '@wordpress/keycodes': 3.6.1 + '@wordpress/keycodes': 3.28.0 '@wordpress/primitives': 3.4.1 - '@wordpress/rich-text': 5.4.2_react@17.0.2 + '@wordpress/rich-text': 5.17.0_react@17.0.2 '@wordpress/warning': 2.6.1 classnames: 2.3.1 colord: 2.9.2 @@ -16381,21 +16408,21 @@ packages: '@emotion/styled': 11.8.1_6t3indjc5ssefvr44gr3wo2uqu '@emotion/utils': 1.0.0 '@use-gesture/react': 10.2.10_react@17.0.2 - '@wordpress/a11y': 3.6.1 - '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/date': 4.6.1 - '@wordpress/deprecated': 3.6.1 - '@wordpress/dom': 3.6.1 - '@wordpress/element': 4.4.1 + '@wordpress/a11y': 3.28.0 + '@wordpress/compose': 5.17.0_react@17.0.2 + '@wordpress/date': 4.28.0 + '@wordpress/deprecated': 3.28.0 + '@wordpress/dom': 3.28.0 + '@wordpress/element': 4.20.0 '@wordpress/escape-html': 2.28.0 - '@wordpress/hooks': 3.6.1 - '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/hooks': 3.28.0 + '@wordpress/i18n': 4.28.0 + '@wordpress/icons': 8.4.0 '@wordpress/is-shallow-equal': 4.28.0 - '@wordpress/keycodes': 3.6.1 - '@wordpress/primitives': 3.4.1 - '@wordpress/rich-text': 5.4.2_react@17.0.2 - '@wordpress/warning': 2.6.1 + '@wordpress/keycodes': 3.28.0 + '@wordpress/primitives': 3.26.0 + '@wordpress/rich-text': 5.17.0_react@17.0.2 + '@wordpress/warning': 2.28.0 classnames: 2.3.1 colord: 2.9.2 dom-scroll-into-view: 1.2.1 @@ -16435,21 +16462,21 @@ packages: '@emotion/styled': 11.8.1_c2qm47vaialpqni522adyu6za4 '@emotion/utils': 1.0.0 '@use-gesture/react': 10.2.10_react@17.0.2 - '@wordpress/a11y': 3.6.1 - '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/date': 4.6.1 - '@wordpress/deprecated': 3.6.1 - '@wordpress/dom': 3.6.1 - '@wordpress/element': 4.4.1 + '@wordpress/a11y': 3.28.0 + '@wordpress/compose': 5.17.0_react@17.0.2 + '@wordpress/date': 4.28.0 + '@wordpress/deprecated': 3.28.0 + '@wordpress/dom': 3.28.0 + '@wordpress/element': 4.20.0 '@wordpress/escape-html': 2.28.0 - '@wordpress/hooks': 3.6.1 - '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/hooks': 3.28.0 + '@wordpress/i18n': 4.28.0 + '@wordpress/icons': 8.4.0 '@wordpress/is-shallow-equal': 4.28.0 - '@wordpress/keycodes': 3.6.1 - '@wordpress/primitives': 3.4.1 - '@wordpress/rich-text': 5.4.2_react@17.0.2 - '@wordpress/warning': 2.6.1 + '@wordpress/keycodes': 3.28.0 + '@wordpress/primitives': 3.26.0 + '@wordpress/rich-text': 5.17.0_react@17.0.2 + '@wordpress/warning': 2.28.0 classnames: 2.3.1 colord: 2.9.2 dom-scroll-into-view: 1.2.1 @@ -16489,21 +16516,21 @@ packages: '@emotion/styled': 11.8.1_c2qm47vaialpqni522adyu6za4 '@emotion/utils': 1.0.0 '@use-gesture/react': 10.2.10_react@17.0.2 - '@wordpress/a11y': 3.6.1 - '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/date': 4.6.1 - '@wordpress/deprecated': 3.6.1 - '@wordpress/dom': 3.6.1 - '@wordpress/element': 4.4.1 + '@wordpress/a11y': 3.28.0 + '@wordpress/compose': 5.17.0_react@17.0.2 + '@wordpress/date': 4.28.0 + '@wordpress/deprecated': 3.28.0 + '@wordpress/dom': 3.28.0 + '@wordpress/element': 4.20.0 '@wordpress/escape-html': 2.28.0 - '@wordpress/hooks': 3.6.1 - '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/hooks': 3.28.0 + '@wordpress/i18n': 4.28.0 + '@wordpress/icons': 8.4.0 '@wordpress/is-shallow-equal': 4.28.0 - '@wordpress/keycodes': 3.6.1 - '@wordpress/primitives': 3.4.1 - '@wordpress/rich-text': 5.4.2_react@17.0.2 - '@wordpress/warning': 2.6.1 + '@wordpress/keycodes': 3.28.0 + '@wordpress/primitives': 3.26.0 + '@wordpress/rich-text': 5.17.0_react@17.0.2 + '@wordpress/warning': 2.28.0 classnames: 2.3.1 colord: 2.9.2 dom-scroll-into-view: 1.2.1 @@ -16543,21 +16570,21 @@ packages: '@emotion/styled': 11.8.1_hhesyqfwklnojgamcachhyxace '@emotion/utils': 1.0.0 '@use-gesture/react': 10.2.10_react@17.0.2 - '@wordpress/a11y': 3.6.1 - '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/date': 4.6.1 - '@wordpress/deprecated': 3.6.1 - '@wordpress/dom': 3.6.1 - '@wordpress/element': 4.4.1 + '@wordpress/a11y': 3.28.0 + '@wordpress/compose': 5.17.0_react@17.0.2 + '@wordpress/date': 4.28.0 + '@wordpress/deprecated': 3.28.0 + '@wordpress/dom': 3.28.0 + '@wordpress/element': 4.20.0 '@wordpress/escape-html': 2.28.0 - '@wordpress/hooks': 3.6.1 - '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/hooks': 3.28.0 + '@wordpress/i18n': 4.28.0 + '@wordpress/icons': 8.4.0 '@wordpress/is-shallow-equal': 4.28.0 - '@wordpress/keycodes': 3.6.1 - '@wordpress/primitives': 3.4.1 - '@wordpress/rich-text': 5.4.2_react@17.0.2 - '@wordpress/warning': 2.6.1 + '@wordpress/keycodes': 3.28.0 + '@wordpress/primitives': 3.26.0 + '@wordpress/rich-text': 5.17.0_react@17.0.2 + '@wordpress/warning': 2.28.0 classnames: 2.3.1 colord: 2.9.2 dom-scroll-into-view: 1.2.1 @@ -16861,11 +16888,11 @@ packages: '@babel/runtime': 7.21.0 '@types/lodash': 4.14.184 '@types/mousetrap': 1.6.9 - '@wordpress/deprecated': 3.6.1 - '@wordpress/dom': 3.6.1 - '@wordpress/element': 4.4.1 + '@wordpress/deprecated': 3.28.0 + '@wordpress/dom': 3.28.0 + '@wordpress/element': 4.20.0 '@wordpress/is-shallow-equal': 4.28.0 - '@wordpress/keycodes': 3.6.1 + '@wordpress/keycodes': 3.28.0 '@wordpress/priority-queue': 2.28.0 clipboard: 2.0.10 lodash: 4.17.21 @@ -16873,6 +16900,7 @@ packages: react: 17.0.2 react-resize-aware: 3.1.1_react@17.0.2 use-memo-one: 1.1.2_react@17.0.2 + dev: false /@wordpress/compose/6.5.0_react@17.0.2: resolution: {integrity: sha512-gtZwEeFFHGltsr0vqwyrxPbAcA6lVfE36s59mZBh9KHeC/s590q2FPQz+9jSE5Y+uQmnXZCtahCrjvnpnaBIUg==} @@ -16901,15 +16929,15 @@ packages: react: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/api-fetch': 6.3.1 + '@wordpress/api-fetch': 6.25.0 '@wordpress/blocks': 11.18.0_react@17.0.2 - '@wordpress/data': 6.6.1_react@17.0.2 - '@wordpress/deprecated': 3.6.1 - '@wordpress/element': 4.4.1 - '@wordpress/html-entities': 3.6.1 - '@wordpress/i18n': 4.6.1 + '@wordpress/data': 6.15.0_react@17.0.2 + '@wordpress/deprecated': 3.28.0 + '@wordpress/element': 4.20.0 + '@wordpress/html-entities': 3.28.0 + '@wordpress/i18n': 4.28.0 '@wordpress/is-shallow-equal': 4.28.0 - '@wordpress/url': 3.7.1 + '@wordpress/url': 3.29.0 equivalent-key-map: 0.2.2 lodash: 4.17.21 memize: 1.1.0 @@ -16986,9 +17014,9 @@ packages: react: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/api-fetch': 6.3.1 - '@wordpress/data': 6.6.1_react@17.0.2 - '@wordpress/deprecated': 3.6.1 + '@wordpress/api-fetch': 6.25.0 + '@wordpress/data': 6.15.0_react@17.0.2 + '@wordpress/deprecated': 3.28.0 react: 17.0.2 dev: false @@ -17043,9 +17071,9 @@ packages: react: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/deprecated': 3.6.1 - '@wordpress/element': 4.4.1 + '@wordpress/compose': 5.17.0_react@17.0.2 + '@wordpress/deprecated': 3.28.0 + '@wordpress/element': 4.20.0 '@wordpress/is-shallow-equal': 4.28.0 '@wordpress/priority-queue': 2.28.0 '@wordpress/redux-routine': 4.28.0_redux@4.2.0 @@ -17158,7 +17186,8 @@ packages: engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - '@wordpress/hooks': 3.6.1 + '@wordpress/hooks': 3.28.0 + dev: false /@wordpress/dom-ready/3.28.0: resolution: {integrity: sha512-PFFAnuPUouV0uSDZN6G/B8yksybtxzS/6H53OZJEA3h3EsNCicKRMGSowkumvLwXA23HV0K2Kht6JuS+bDECzA==} @@ -17192,6 +17221,7 @@ packages: dependencies: '@babel/runtime': 7.21.0 lodash: 4.17.21 + dev: false /@wordpress/e2e-test-utils/3.0.0_ddjhsfu4aotkh3cuzmpsln6ywq: resolution: {integrity: sha512-XMdR8DeKyDQRF5jKeUlOzP4pTRtoJuOLsNZRLUFUvnrs9y/7/hH17VmPbWp3TJGvV/eGKzO4+D+wJTsP9nJmIw==} @@ -17238,9 +17268,9 @@ packages: puppeteer-core: '>=11' dependencies: '@babel/runtime': 7.21.0 - '@wordpress/api-fetch': 6.3.1 - '@wordpress/keycodes': 3.6.1 - '@wordpress/url': 3.7.1 + '@wordpress/api-fetch': 6.25.0 + '@wordpress/keycodes': 3.28.0 + '@wordpress/url': 3.29.0 form-data: 4.0.0 jest: 27.5.1 lodash: 4.17.21 @@ -17258,30 +17288,30 @@ packages: react-dom: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/a11y': 3.6.1 - '@wordpress/api-fetch': 6.3.1 + '@wordpress/a11y': 3.28.0 + '@wordpress/api-fetch': 6.25.0 '@wordpress/block-editor': 8.6.0_eqi5qhcxfphl6j3pngzexvnehi '@wordpress/blocks': 11.18.0_react@17.0.2 - '@wordpress/components': 19.8.5_eqi5qhcxfphl6j3pngzexvnehi - '@wordpress/compose': 5.4.1_react@17.0.2 + '@wordpress/components': 19.12.0_eqi5qhcxfphl6j3pngzexvnehi + '@wordpress/compose': 5.17.0_react@17.0.2 '@wordpress/core-data': 4.4.5_react@17.0.2 - '@wordpress/data': 6.6.1_react@17.0.2 - '@wordpress/date': 4.6.1 - '@wordpress/deprecated': 3.6.1 - '@wordpress/element': 4.4.1 - '@wordpress/hooks': 3.6.1 - '@wordpress/html-entities': 3.6.1 - '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 - '@wordpress/keyboard-shortcuts': 3.4.1_react@17.0.2 - '@wordpress/keycodes': 3.6.1 + '@wordpress/data': 6.15.0_react@17.0.2 + '@wordpress/date': 4.28.0 + '@wordpress/deprecated': 3.28.0 + '@wordpress/element': 4.20.0 + '@wordpress/hooks': 3.28.0 + '@wordpress/html-entities': 3.28.0 + '@wordpress/i18n': 4.28.0 + '@wordpress/icons': 8.4.0 + '@wordpress/keyboard-shortcuts': 3.17.0_react@17.0.2 + '@wordpress/keycodes': 3.28.0 '@wordpress/media-utils': 3.4.1 - '@wordpress/notices': 3.6.1_react@17.0.2 + '@wordpress/notices': 3.28.0_react@17.0.2 '@wordpress/preferences': 1.3.0_eqi5qhcxfphl6j3pngzexvnehi '@wordpress/reusable-blocks': 3.17.0_mtk4wljkd5jimhszw4p7nnxuzm - '@wordpress/rich-text': 5.4.2_react@17.0.2 + '@wordpress/rich-text': 5.17.0_react@17.0.2 '@wordpress/server-side-render': 3.17.0_mtk4wljkd5jimhszw4p7nnxuzm - '@wordpress/url': 3.7.1 + '@wordpress/url': 3.29.0 '@wordpress/wordcount': 3.28.0 classnames: 2.3.1 lodash: 4.17.21 @@ -17349,6 +17379,7 @@ packages: lodash: 4.17.21 react: 17.0.2 react-dom: 17.0.2_react@17.0.2 + dev: false /@wordpress/element/4.8.0: resolution: {integrity: sha512-f2Mb70xvGxZWNWh5pFhOoRgrd+tKs9Xk9hpDgRB7iPel/zbAIxNebr0Jqm5Nt+MDiDl/dogTPc9GyrkYCm9u0g==} @@ -17580,6 +17611,7 @@ packages: engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 + dev: false /@wordpress/html-entities/3.28.0: resolution: {integrity: sha512-UAaU6au8UTrSkowkV33pE/EvdPov2mA9W51vh6t88KsJPzt4171EzIchGnQuzt04HuZLdLyWy2A+7JCOSbfhBA==} @@ -17624,7 +17656,7 @@ packages: hasBin: true dependencies: '@babel/runtime': 7.21.0 - '@wordpress/hooks': 3.6.1 + '@wordpress/hooks': 3.28.0 gettext-parser: 1.4.0 lodash: 4.17.21 memize: 1.1.0 @@ -17638,19 +17670,20 @@ packages: hasBin: true dependencies: '@babel/runtime': 7.21.0 - '@wordpress/hooks': 3.6.1 + '@wordpress/hooks': 3.28.0 gettext-parser: 1.4.0 lodash: 4.17.21 memize: 1.1.0 sprintf-js: 1.1.2 tannin: 1.2.0 + dev: false /@wordpress/icons/8.1.0: resolution: {integrity: sha512-fNq0Mnzzf03uxIwKqQeU/G48wElyypwkhcBZWYQRpmwLZrOR231dxUeK9mzPOSGlYbgM+YKK+k3lzysGmrJU0A==} engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - '@wordpress/element': 4.4.1 + '@wordpress/element': 4.20.0 '@wordpress/primitives': 3.4.1 dev: false @@ -17659,8 +17692,8 @@ packages: engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - '@wordpress/element': 4.4.1 - '@wordpress/primitives': 3.4.1 + '@wordpress/element': 4.20.0 + '@wordpress/primitives': 3.26.0 dev: false /@wordpress/icons/8.4.0: @@ -17688,17 +17721,17 @@ packages: react-dom: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/a11y': 3.6.1 - '@wordpress/components': 19.8.5_eqi5qhcxfphl6j3pngzexvnehi - '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/data': 6.6.1_react@17.0.2 - '@wordpress/deprecated': 3.6.1 - '@wordpress/element': 4.4.1 - '@wordpress/i18n': 4.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/a11y': 3.28.0 + '@wordpress/components': 19.12.0_eqi5qhcxfphl6j3pngzexvnehi + '@wordpress/compose': 5.17.0_react@17.0.2 + '@wordpress/data': 6.15.0_react@17.0.2 + '@wordpress/deprecated': 3.28.0 + '@wordpress/element': 4.20.0 + '@wordpress/i18n': 4.28.0 + '@wordpress/icons': 8.4.0 '@wordpress/plugins': 4.4.3_react@17.0.2 '@wordpress/preferences': 1.3.0_eqi5qhcxfphl6j3pngzexvnehi - '@wordpress/viewport': 4.4.1_react@17.0.2 + '@wordpress/viewport': 4.17.0_react@17.0.2 classnames: 2.3.1 lodash: 4.17.21 react: 17.0.2 @@ -17880,9 +17913,9 @@ packages: react: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/data': 6.6.1_react@17.0.2 - '@wordpress/element': 4.4.1 - '@wordpress/keycodes': 3.6.1 + '@wordpress/data': 6.15.0_react@17.0.2 + '@wordpress/element': 4.20.0 + '@wordpress/keycodes': 3.28.0 lodash: 4.17.21 react: 17.0.2 rememo: 3.0.0 @@ -17908,7 +17941,7 @@ packages: engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - '@wordpress/i18n': 4.6.1 + '@wordpress/i18n': 4.28.0 lodash: 4.17.21 dev: false @@ -17917,18 +17950,19 @@ packages: engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - '@wordpress/i18n': 4.6.1 + '@wordpress/i18n': 4.28.0 lodash: 4.17.21 + dev: false /@wordpress/media-utils/3.4.1: resolution: {integrity: sha512-WNAaMqqw6Eqy61KTWBy1NlgXSZH82Cm2SPVbz0v6yhJ4ktJmSRFm7Fd4mTMFS/L7NKTxwo+DFqEHlTGKj3lyzQ==} engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - '@wordpress/api-fetch': 6.3.1 + '@wordpress/api-fetch': 6.25.0 '@wordpress/blob': 3.28.0 - '@wordpress/element': 4.4.1 - '@wordpress/i18n': 4.6.1 + '@wordpress/element': 4.20.0 + '@wordpress/i18n': 4.28.0 lodash: 4.17.21 dev: false @@ -17949,8 +17983,8 @@ packages: react: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/a11y': 3.6.1 - '@wordpress/data': 6.6.1_react@17.0.2 + '@wordpress/a11y': 3.28.0 + '@wordpress/data': 6.15.0_react@17.0.2 lodash: 4.17.21 react: 17.0.2 dev: false @@ -17980,10 +18014,10 @@ packages: react: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/element': 4.4.1 - '@wordpress/hooks': 3.6.1 - '@wordpress/icons': 8.2.3 + '@wordpress/compose': 5.17.0_react@17.0.2 + '@wordpress/element': 4.20.0 + '@wordpress/hooks': 3.28.0 + '@wordpress/icons': 8.4.0 lodash: 4.17.21 memize: 1.1.0 react: 17.0.2 @@ -18016,7 +18050,7 @@ packages: peerDependencies: postcss: ^8.0.0 dependencies: - '@wordpress/base-styles': 4.3.1 + '@wordpress/base-styles': 4.8.0 autoprefixer: 10.4.4_postcss@8.4.21 postcss: 8.4.21 dev: false @@ -18129,7 +18163,7 @@ packages: engines: {node: '>=12'} dependencies: '@babel/runtime': 7.21.0 - '@wordpress/element': 4.4.1 + '@wordpress/element': 4.20.0 classnames: 2.3.1 dev: false @@ -18275,13 +18309,13 @@ packages: react: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/a11y': 3.6.1 - '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/data': 6.6.1_react@17.0.2 - '@wordpress/element': 4.4.1 + '@wordpress/a11y': 3.28.0 + '@wordpress/compose': 5.17.0_react@17.0.2 + '@wordpress/data': 6.15.0_react@17.0.2 + '@wordpress/element': 4.20.0 '@wordpress/escape-html': 2.28.0 - '@wordpress/i18n': 4.6.1 - '@wordpress/keycodes': 3.6.1 + '@wordpress/i18n': 4.28.0 + '@wordpress/keycodes': 3.28.0 lodash: 4.17.21 memize: 1.1.0 react: 17.0.2 @@ -18607,6 +18641,7 @@ packages: dependencies: '@babel/runtime': 7.21.0 lodash: 4.17.21 + dev: false /@wordpress/viewport/4.17.0_react@17.0.2: resolution: {integrity: sha512-5FZCqXjsZjONoyCfjalRgdme//j4XJYHRXYh7ynoJW/qULq3YqZhyLtjFsEM4V+uuuURFSYnGnOD7V+K9wooPA==} @@ -18628,8 +18663,8 @@ packages: react: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/data': 6.6.1_react@17.0.2 + '@wordpress/compose': 5.17.0_react@17.0.2 + '@wordpress/data': 6.15.0_react@17.0.2 lodash: 4.17.21 react: 17.0.2 dev: false @@ -18641,8 +18676,8 @@ packages: react: ^17.0.0 dependencies: '@babel/runtime': 7.21.0 - '@wordpress/compose': 5.4.1_react@17.0.2 - '@wordpress/data': 6.6.1_react@17.0.2 + '@wordpress/compose': 5.17.0_react@17.0.2 + '@wordpress/data': 6.15.0_react@17.0.2 lodash: 4.17.21 react: 17.0.2 dev: false @@ -18662,6 +18697,7 @@ packages: /@wordpress/warning/2.6.1: resolution: {integrity: sha512-Xs37x0IkvNewPNKs1A8cnw5xLb+AqwUqqCsH4+5Sjat5GDqP86mHgLfRIlE4d6fBYg+q6tO7DVPG49TT3/wzgA==} engines: {node: '>=12'} + dev: false /@wordpress/wordcount/3.19.0: resolution: {integrity: sha512-x5M997RMrglq/XiGi55sO4fIPrGu20bob6h5goc9NKbbq68NTTDPrznfRbhQ+gTLLEV79AtUO/RPK3y9V9Pvkw==} @@ -19491,7 +19527,7 @@ packages: /axios/0.21.4: resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} dependencies: - follow-redirects: 1.14.7 + follow-redirects: 1.14.7_debug@4.3.3 transitivePeerDependencies: - debug @@ -20877,7 +20913,7 @@ packages: resolution: {integrity: sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==} hasBin: true dependencies: - caniuse-lite: 1.0.30001146 + caniuse-lite: 1.0.30001418 electron-to-chromium: 1.4.276 dev: true @@ -21218,13 +21254,14 @@ packages: /caniuse-api/3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: - browserslist: 4.19.3 - caniuse-lite: 1.0.30001146 + browserslist: 4.21.4 + caniuse-lite: 1.0.30001418 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 /caniuse-lite/1.0.30001146: resolution: {integrity: sha512-VAy5RHDfTJhpxnDdp2n40GPPLp3KqNrXz1QqFv4J64HvArKs8nuNMOWkB3ICOaBTU/Aj4rYAo/ytdQDDFF/Pug==} + dev: true /caniuse-lite/1.0.30001352: resolution: {integrity: sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA==} @@ -24296,9 +24333,9 @@ packages: has: 1.0.3 is-core-module: 2.8.0 is-glob: 4.0.3 - minimatch: 3.1.2 + minimatch: 3.0.4 object.values: 1.1.5 - resolve: 1.22.1 + resolve: 1.20.0 tsconfig-paths: 3.14.0 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -24327,9 +24364,9 @@ packages: has: 1.0.3 is-core-module: 2.8.0 is-glob: 4.0.3 - minimatch: 3.1.2 + minimatch: 3.0.4 object.values: 1.1.5 - resolve: 1.22.1 + resolve: 1.20.0 tsconfig-paths: 3.14.0 transitivePeerDependencies: - eslint-import-resolver-typescript @@ -25944,15 +25981,6 @@ packages: debug: optional: true - /follow-redirects/1.14.7: - resolution: {integrity: sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - /follow-redirects/1.14.7_debug@4.3.3: resolution: {integrity: sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==} engines: {node: '>=4.0'} @@ -25963,7 +25991,6 @@ packages: optional: true dependencies: debug: 4.3.3 - dev: true /follow-redirects/1.5.10: resolution: {integrity: sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==} @@ -26112,18 +26139,18 @@ packages: vue-template-compiler: optional: true dependencies: - '@babel/code-frame': 7.18.6 + '@babel/code-frame': 7.16.7 '@types/json-schema': 7.0.9 chalk: 4.1.2 chokidar: 3.5.3 cosmiconfig: 6.0.0 - deepmerge: 4.3.0 + deepmerge: 4.2.2 fs-extra: 9.1.0 glob: 7.2.3 memfs: 3.3.0 minimatch: 3.1.2 schema-utils: 2.7.0 - semver: 7.3.8 + semver: 7.3.5 tapable: 1.1.3 typescript: 4.9.5 webpack: 5.70.0 @@ -26143,18 +26170,18 @@ packages: vue-template-compiler: optional: true dependencies: - '@babel/code-frame': 7.18.6 + '@babel/code-frame': 7.16.7 '@types/json-schema': 7.0.9 chalk: 4.1.2 chokidar: 3.5.3 cosmiconfig: 6.0.0 - deepmerge: 4.3.0 + deepmerge: 4.2.2 fs-extra: 9.1.0 glob: 7.2.3 memfs: 3.3.0 minimatch: 3.1.2 schema-utils: 2.7.0 - semver: 7.3.8 + semver: 7.3.5 tapable: 1.1.3 typescript: 4.9.5 webpack: 4.46.0 @@ -26174,19 +26201,19 @@ packages: vue-template-compiler: optional: true dependencies: - '@babel/code-frame': 7.18.6 + '@babel/code-frame': 7.16.7 '@types/json-schema': 7.0.9 chalk: 4.1.2 chokidar: 3.5.3 cosmiconfig: 6.0.0 - deepmerge: 4.3.0 + deepmerge: 4.2.2 eslint: 8.32.0 fs-extra: 9.1.0 glob: 7.2.3 memfs: 3.3.0 minimatch: 3.1.2 schema-utils: 2.7.0 - semver: 7.3.8 + semver: 7.3.5 tapable: 1.1.3 typescript: 4.9.5 webpack: 4.46.0_webpack-cli@3.3.12 @@ -27905,7 +27932,7 @@ packages: '@automattic/interpolate-components': 1.2.1_pxzommwrsowkd4kgag6q3sluym '@babel/runtime': 7.21.0 '@tannin/sprintf': 1.2.0 - '@wordpress/compose': 5.4.1_react@17.0.2 + '@wordpress/compose': 5.17.0_react@17.0.2 debug: 4.3.4 events: 3.3.0 hash.js: 1.1.7 @@ -32889,7 +32916,6 @@ packages: resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} dependencies: brace-expansion: 1.1.11 - dev: true /minimatch/3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -34713,7 +34739,7 @@ packages: resolution: {integrity: sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==} engines: {node: '>=6.9.0'} dependencies: - browserslist: 4.19.3 + browserslist: 4.21.4 color: 3.2.1 has: 1.0.3 postcss: 7.0.39 @@ -34725,7 +34751,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.3 + browserslist: 4.21.4 caniuse-api: 3.0.0 colord: 2.9.2 postcss: 8.4.12 @@ -34889,7 +34915,7 @@ packages: postcss: 8.4.21 schema-utils: 3.1.1 semver: 7.3.8 - webpack: 5.70.0_webpack-cli@3.3.12 + webpack: 5.70.0 /postcss-loader/4.3.0_twwyhqqim6liv4fz2ggv7g4m5a: resolution: {integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==} @@ -34965,7 +34991,7 @@ packages: resolution: {integrity: sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==} engines: {node: '>=6.9.0'} dependencies: - browserslist: 4.19.3 + browserslist: 4.21.4 caniuse-api: 3.0.0 cssnano-util-same-parent: 4.0.1 postcss: 7.0.39 @@ -34978,7 +35004,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.3 + browserslist: 4.21.4 caniuse-api: 3.0.0 cssnano-utils: 3.1.0_postcss@8.4.12 postcss: 8.4.12 @@ -35032,7 +35058,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: alphanum-sort: 1.0.2 - browserslist: 4.19.3 + browserslist: 4.21.4 cssnano-util-get-arguments: 4.0.0 postcss: 7.0.39 postcss-value-parser: 3.3.1 @@ -35044,7 +35070,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.3 + browserslist: 4.21.4 cssnano-utils: 3.1.0_postcss@8.4.12 postcss: 8.4.12 postcss-value-parser: 4.2.0 @@ -35290,7 +35316,7 @@ packages: resolution: {integrity: sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==} engines: {node: '>=6.9.0'} dependencies: - browserslist: 4.19.3 + browserslist: 4.21.4 postcss: 7.0.39 postcss-value-parser: 3.3.1 @@ -35300,7 +35326,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.3 + browserslist: 4.21.4 postcss: 8.4.12 postcss-value-parser: 4.2.0 dev: true @@ -35365,7 +35391,7 @@ packages: resolution: {integrity: sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==} engines: {node: '>=6.9.0'} dependencies: - browserslist: 4.19.3 + browserslist: 4.21.4 caniuse-api: 3.0.0 has: 1.0.3 postcss: 7.0.39 @@ -35376,7 +35402,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.3 + browserslist: 4.21.4 caniuse-api: 3.0.0 postcss: 8.4.12 dev: true @@ -36364,7 +36390,7 @@ packages: is-touch-device: 1.0.1 lodash: 4.17.21 moment: 2.29.4 - object.assign: 4.1.2 + object.assign: 4.1.4 object.values: 1.1.5 prop-types: 15.8.1 raf: 3.4.1 @@ -39264,7 +39290,7 @@ packages: resolution: {integrity: sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==} engines: {node: '>=6.9.0'} dependencies: - browserslist: 4.19.3 + browserslist: 4.21.4 postcss: 7.0.39 postcss-selector-parser: 3.1.2 @@ -39274,7 +39300,7 @@ packages: peerDependencies: postcss: ^8.2.15 dependencies: - browserslist: 4.19.3 + browserslist: 4.21.4 postcss: 8.4.12 postcss-selector-parser: 6.0.9 dev: true From f5591b7c9a610d53d1d9b4959fbce00ca05b71eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maikel=20David=20P=C3=A9rez=20G=C3=B3mez?= Date: Tue, 11 Apr 2023 14:52:20 -0400 Subject: [PATCH 174/466] Add track inventory field to inventory section (#37585) --- .../js/product-editor/changelog/add-37400 | 4 + .../src/blocks/track-inventory/block.json | 25 +++++ .../src/blocks/track-inventory/edit.tsx | 103 ++++++++++++++++++ .../src/blocks/track-inventory/editor.scss | 15 +++ .../src/blocks/track-inventory/index.ts | 29 +++++ .../src/blocks/track-inventory/types.ts | 8 ++ .../src/components/editor/init-blocks.ts | 2 + packages/js/product-editor/src/style.scss | 1 + plugins/woocommerce/changelog/add-37400 | 4 + .../includes/class-wc-post-types.php | 6 + 10 files changed, 197 insertions(+) create mode 100644 packages/js/product-editor/changelog/add-37400 create mode 100644 packages/js/product-editor/src/blocks/track-inventory/block.json create mode 100644 packages/js/product-editor/src/blocks/track-inventory/edit.tsx create mode 100644 packages/js/product-editor/src/blocks/track-inventory/editor.scss create mode 100644 packages/js/product-editor/src/blocks/track-inventory/index.ts create mode 100644 packages/js/product-editor/src/blocks/track-inventory/types.ts create mode 100644 plugins/woocommerce/changelog/add-37400 diff --git a/packages/js/product-editor/changelog/add-37400 b/packages/js/product-editor/changelog/add-37400 new file mode 100644 index 00000000000..42330a9fdbf --- /dev/null +++ b/packages/js/product-editor/changelog/add-37400 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add product track inventory block diff --git a/packages/js/product-editor/src/blocks/track-inventory/block.json b/packages/js/product-editor/src/blocks/track-inventory/block.json new file mode 100644 index 00000000000..bee522393cc --- /dev/null +++ b/packages/js/product-editor/src/blocks/track-inventory/block.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "woocommerce/product-track-inventory-fields", + "title": "Product track inventory fields", + "category": "woocommerce", + "description": "The product track inventory fields.", + "keywords": [ "products", "track", "inventory" ], + "textdomain": "default", + "attributes": { + "name": { + "type": "string", + "__experimentalRole": "content" + } + }, + "supports": { + "align": false, + "html": false, + "multiple": false, + "reusable": false, + "inserter": false, + "lock": false + }, + "editorStyle": "file:./editor.css" +} diff --git a/packages/js/product-editor/src/blocks/track-inventory/edit.tsx b/packages/js/product-editor/src/blocks/track-inventory/edit.tsx new file mode 100644 index 00000000000..a08015ae0c7 --- /dev/null +++ b/packages/js/product-editor/src/blocks/track-inventory/edit.tsx @@ -0,0 +1,103 @@ +/** + * External dependencies + */ +import { BlockEditProps } from '@wordpress/blocks'; +import { useBlockProps } from '@wordpress/block-editor'; +import { useInstanceId } from '@wordpress/compose'; +import { useEntityProp } from '@wordpress/core-data'; +import { createElement, useEffect } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { + BaseControl, + ToggleControl, + // @ts-expect-error `__experimentalInputControl` does exist. + __experimentalInputControl as InputControl, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { TrackInventoryBlockAttributes } from './types'; +import { useValidation } from '../../hooks/use-validation'; + +export function Edit( {}: BlockEditProps< TrackInventoryBlockAttributes > ) { + const blockProps = useBlockProps( { + className: 'wp-block-woocommerce-product-track-inventory-fields', + } ); + + const [ manageStock, setManageStock ] = useEntityProp< boolean >( + 'postType', + 'product', + 'manage_stock' + ); + + const [ stockQuantity, setStockQuantity ] = useEntityProp< number | null >( + 'postType', + 'product', + 'stock_quantity' + ); + + const stockQuantityId = useInstanceId( BaseControl ) as string; + + const isStockQuantityValid = useValidation( + 'product/stock_quantity', + function stockQuantityValidator() { + if ( ! manageStock ) return true; + return Boolean( stockQuantity && stockQuantity >= 0 ); + } + ); + + useEffect( () => { + if ( manageStock && stockQuantity === null ) { + setStockQuantity( 1 ); + } + }, [ manageStock, stockQuantity ] ); + + return ( +
    + + + { manageStock && ( +
    +
    + + + +
    + +
    +
    + ) } +
    + ); +} diff --git a/packages/js/product-editor/src/blocks/track-inventory/editor.scss b/packages/js/product-editor/src/blocks/track-inventory/editor.scss new file mode 100644 index 00000000000..28ed723ca6b --- /dev/null +++ b/packages/js/product-editor/src/blocks/track-inventory/editor.scss @@ -0,0 +1,15 @@ +.wp-block-woocommerce-product-track-inventory-fields { + .components-toggle-control { + margin-bottom: $gap-large; + } + + .has-error { + .components-base-control .components-input-control__backdrop { + border-color: $studio-red-50; + } + + .components-base-control__help { + color: $studio-red-50; + } + } +} diff --git a/packages/js/product-editor/src/blocks/track-inventory/index.ts b/packages/js/product-editor/src/blocks/track-inventory/index.ts new file mode 100644 index 00000000000..d52ac90ea2f --- /dev/null +++ b/packages/js/product-editor/src/blocks/track-inventory/index.ts @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { BlockConfiguration } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { initBlock } from '../../utils/init-blocks'; +import blockConfiguration from './block.json'; +import { Edit } from './edit'; +import { TrackInventoryBlockAttributes } from './types'; + +const { name, ...metadata } = + blockConfiguration as BlockConfiguration< TrackInventoryBlockAttributes >; + +export { metadata, name }; + +export const settings: Partial< + BlockConfiguration< TrackInventoryBlockAttributes > +> = { + example: {}, + edit: Edit, +}; + +export function init() { + return initBlock( { name, metadata, settings } ); +} diff --git a/packages/js/product-editor/src/blocks/track-inventory/types.ts b/packages/js/product-editor/src/blocks/track-inventory/types.ts new file mode 100644 index 00000000000..9e39fa898e3 --- /dev/null +++ b/packages/js/product-editor/src/blocks/track-inventory/types.ts @@ -0,0 +1,8 @@ +/** + * External dependencies + */ +import { BlockAttributes } from '@wordpress/blocks'; + +export interface TrackInventoryBlockAttributes extends BlockAttributes { + name: string; +} diff --git a/packages/js/product-editor/src/components/editor/init-blocks.ts b/packages/js/product-editor/src/components/editor/init-blocks.ts index c81c2aa288a..ea4d0128b1d 100644 --- a/packages/js/product-editor/src/components/editor/init-blocks.ts +++ b/packages/js/product-editor/src/components/editor/init-blocks.ts @@ -21,6 +21,7 @@ import { init as initTab } from '../tab'; import { init as initPricing } from '../pricing-block'; import { init as initCollapsible } from '../collapsible-block'; import { init as initScheduleSale } from '../../blocks/schedule-sale'; +import { init as initTrackInventory } from '../../blocks/track-inventory'; export const initBlocks = () => { const coreBlocks = __experimentalGetCoreBlocks(); @@ -40,4 +41,5 @@ export const initBlocks = () => { initPricing(); initCollapsible(); initScheduleSale(); + initTrackInventory(); }; diff --git a/packages/js/product-editor/src/style.scss b/packages/js/product-editor/src/style.scss index 7e2107e3fdb..be7eb118202 100644 --- a/packages/js/product-editor/src/style.scss +++ b/packages/js/product-editor/src/style.scss @@ -1,4 +1,5 @@ @import 'blocks/schedule-sale/editor.scss'; +@import 'blocks/track-inventory/editor.scss'; @import 'components/editor/style.scss'; @import 'components/product-section-layout/style.scss'; @import 'components/edit-product-link-modal/style.scss'; diff --git a/plugins/woocommerce/changelog/add-37400 b/plugins/woocommerce/changelog/add-37400 new file mode 100644 index 00000000000..f4d1ba1232e --- /dev/null +++ b/plugins/woocommerce/changelog/add-37400 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Register product track inventory block diff --git a/plugins/woocommerce/includes/class-wc-post-types.php b/plugins/woocommerce/includes/class-wc-post-types.php index 19b5093c99e..e2c74e2afe7 100644 --- a/plugins/woocommerce/includes/class-wc-post-types.php +++ b/plugins/woocommerce/includes/class-wc-post-types.php @@ -584,6 +584,12 @@ class WC_Post_Types { 'id' => 'inventory', 'title' => __( 'Inventory', 'woocommerce' ), ), + array( + array( + 'woocommerce/product-track-inventory-fields', + array(), + ), + ), ), array( 'woocommerce/product-tab', From 30536f636ed8b5b2acb57d40ab37c3af8d900084 Mon Sep 17 00:00:00 2001 From: Moon Date: Tue, 11 Apr 2023 12:22:15 -0700 Subject: [PATCH 175/466] Check min. WP and PHP versions before suggesting plugins (#37611) * Add min_php_version for google and pinterest extensions * Hide extensions that do not meet the min php version requirement. * Add min_wp_version * Add changelog * Revert code style --- ...ate-16525-check-php-version-for-extensions | 4 +++ .../DefaultFreeExtensions.php | 26 +++++++++++-------- .../EvaluateExtension.php | 9 +++++++ 3 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 plugins/woocommerce/changelog/update-16525-check-php-version-for-extensions diff --git a/plugins/woocommerce/changelog/update-16525-check-php-version-for-extensions b/plugins/woocommerce/changelog/update-16525-check-php-version-for-extensions new file mode 100644 index 00000000000..9cb44346db3 --- /dev/null +++ b/plugins/woocommerce/changelog/update-16525-check-php-version-for-extensions @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Support min_php_version and min_wp_version for the free extensions feed diff --git a/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensions.php b/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensions.php index 99055062238..e893afd283e 100644 --- a/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensions.php +++ b/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensions.php @@ -79,17 +79,18 @@ class DefaultFreeExtensions { public static function get_plugin( $slug ) { $plugins = array( 'google-listings-and-ads' => [ - 'name' => __( 'Google Listings & Ads', 'woocommerce' ), - 'description' => sprintf( + 'min_php_version' => '7.4', + 'name' => __( 'Google Listings & Ads', 'woocommerce' ), + 'description' => sprintf( /* translators: 1: opening product link tag. 2: closing link tag */ __( 'Drive sales with %1$sGoogle Listings and Ads%2$s', 'woocommerce' ), '', '' ), - 'image_url' => plugins_url( '/assets/images/onboarding/google.svg', WC_PLUGIN_FILE ), - 'manage_url' => 'admin.php?page=wc-admin&path=%2Fgoogle%2Fstart', - 'is_built_by_wc' => true, - 'is_visible' => [ + 'image_url' => plugins_url( '/assets/images/onboarding/google.svg', WC_PLUGIN_FILE ), + 'manage_url' => 'admin.php?page=wc-admin&path=%2Fgoogle%2Fstart', + 'is_built_by_wc' => true, + 'is_visible' => [ [ 'type' => 'not', 'operand' => [ @@ -125,11 +126,12 @@ class DefaultFreeExtensions { 'is_built_by_wc' => false, ], 'pinterest-for-woocommerce' => [ - 'name' => __( 'Pinterest for WooCommerce', 'woocommerce' ), - 'description' => __( 'Get your products in front of Pinners searching for ideas and things to buy.', 'woocommerce' ), - 'image_url' => plugins_url( '/assets/images/onboarding/pinterest.png', WC_PLUGIN_FILE ), - 'manage_url' => 'admin.php?page=wc-admin&path=%2Fpinterest%2Flanding', - 'is_built_by_wc' => true, + 'name' => __( 'Pinterest for WooCommerce', 'woocommerce' ), + 'description' => __( 'Get your products in front of Pinners searching for ideas and things to buy.', 'woocommerce' ), + 'image_url' => plugins_url( '/assets/images/onboarding/pinterest.png', WC_PLUGIN_FILE ), + 'manage_url' => 'admin.php?page=wc-admin&path=%2Fpinterest%2Flanding', + 'is_built_by_wc' => true, + 'min_php_version' => '7.3', ], 'pinterest-for-woocommerce:alt' => [ 'name' => __( 'Pinterest for WooCommerce', 'woocommerce' ), @@ -349,6 +351,7 @@ class DefaultFreeExtensions { DefaultPaymentGateways::get_rules_for_cbd( false ), ], 'is_built_by_wc' => true, + 'min_wp_version' => '5.9', ], 'woocommerce-services:shipping' => [ 'description' => sprintf( @@ -516,6 +519,7 @@ class DefaultFreeExtensions { ], ], 'is_built_by_wc' => false, + 'min_wp_version' => '6.0', ], 'mailpoet' => [ 'name' => __( 'MailPoet', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/EvaluateExtension.php b/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/EvaluateExtension.php index 100dd94a125..8256be19ce3 100644 --- a/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/EvaluateExtension.php +++ b/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/EvaluateExtension.php @@ -21,6 +21,7 @@ class EvaluateExtension { * @return object The evaluated extension. */ public static function evaluate( $extension ) { + global $wp_version; $rule_evaluator = new RuleEvaluator(); if ( isset( $extension->is_visible ) ) { @@ -30,6 +31,14 @@ class EvaluateExtension { $extension->is_visible = true; } + if ( isset( $extension->min_php_version ) ) { + $extension->is_visible = version_compare( PHP_VERSION, $extension->min_php_version, '>=' ); + } + + if ( isset( $extension->min_wp_version ) ) { + $extension->is_visible = version_compare( $wp_version, $extension->min_wp_version, '>=' ); + } + $installed_plugins = PluginsHelper::get_installed_plugin_slugs(); $activated_plugins = PluginsHelper::get_active_plugin_slugs(); $extension->is_installed = in_array( explode( ':', $extension->key )[0], $installed_plugins, true ); From c4a7e9a11b62e9f62fd507f5a0c8ae09740619a0 Mon Sep 17 00:00:00 2001 From: Paul Sealock Date: Wed, 12 Apr 2023 09:26:21 +1200 Subject: [PATCH 176/466] Monorepo CLI tooling: Set folder organisation (#37561) --- .github/workflows/pr-highlight-changes.yml | 2 +- package.json | 8 +- pnpm-lock.yaml | 2107 +++++++++++++++-- pnpm-workspace.yaml | 1 + tools/code-analyzer/.eslintrc.js | 2 +- tools/monorepo-utils/.eslintignore | 0 tools/monorepo-utils/.eslintrc.js | 4 + tools/monorepo-utils/.gitignore | 0 tools/monorepo-utils/README.md | 5 + tools/monorepo-utils/jest.config.js | 6 + tools/monorepo-utils/package.json | 35 + .../monorepo-utils/src/code-freeze/README.md | 3 + .../src/code-freeze/commands/index.ts | 15 + .../src/code-freeze/commands/verify-day.ts | 21 + .../src/code-freeze/utils/__tests__/index.ts | 10 + .../src/code-freeze/utils/index.ts | 3 + tools/monorepo-utils/src/index.ts | 17 + tools/monorepo-utils/tsconfig.json | 15 + 18 files changed, 2118 insertions(+), 136 deletions(-) create mode 100644 tools/monorepo-utils/.eslintignore create mode 100644 tools/monorepo-utils/.eslintrc.js create mode 100644 tools/monorepo-utils/.gitignore create mode 100644 tools/monorepo-utils/README.md create mode 100644 tools/monorepo-utils/jest.config.js create mode 100644 tools/monorepo-utils/package.json create mode 100644 tools/monorepo-utils/src/code-freeze/README.md create mode 100644 tools/monorepo-utils/src/code-freeze/commands/index.ts create mode 100644 tools/monorepo-utils/src/code-freeze/commands/verify-day.ts create mode 100644 tools/monorepo-utils/src/code-freeze/utils/__tests__/index.ts create mode 100644 tools/monorepo-utils/src/code-freeze/utils/index.ts create mode 100755 tools/monorepo-utils/src/index.ts create mode 100644 tools/monorepo-utils/tsconfig.json diff --git a/.github/workflows/pr-highlight-changes.yml b/.github/workflows/pr-highlight-changes.yml index b1ecc977f14..ec9d0c28393 100644 --- a/.github/workflows/pr-highlight-changes.yml +++ b/.github/workflows/pr-highlight-changes.yml @@ -22,7 +22,7 @@ jobs: run: | npm install -g pnpm@7 npm -g i @wordpress/env@5.1.0 - pnpm install --filter code-analyzer --filter cli-core + pnpm install --filter monorepo-utils --filter code-analyzer --filter cli-core - name: Run analyzer id: run working-directory: tools/code-analyzer diff --git a/package.json b/package.json index adb2745dd07..378c21602ee 100644 --- a/package.json +++ b/package.json @@ -17,16 +17,20 @@ "bugs": { "url": "https://github.com/woocommerce/woocommerce/issues" }, + "bin": { + "utils": "./tools/monorepo-utils/dist/index.js" + }, "scripts": { "build": "pnpm exec turbo run turbo:build", "test": "pnpm exec turbo run turbo:test", "clean": "pnpm store prune && git clean -fx **/node_modules && pnpm i", "preinstall": "npx only-allow pnpm", - "postinstall": "pnpm git:update-hooks", + "postinstall": "pnpm git:update-hooks && pnpm run --filter='./tools/monorepo-utils' build", "git:update-hooks": "rm -r .git/hooks && mkdir -p .git/hooks && husky install", "create-extension": "node ./tools/create-extension/index.js", "cherry-pick": "node ./tools/cherry-pick/bin/run", - "sync-dependencies": "pnpm exec syncpack -- fix-mismatches" + "sync-dependencies": "pnpm exec syncpack -- fix-mismatches", + "utils": "./tools/monorepo-utils/dist/index.js" }, "devDependencies": { "@babel/preset-env": "^7.20.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 26415e7da7c..5cc26d71320 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -222,7 +222,7 @@ importers: dotenv: 10.0.0 jest: 27.5.1 jest-allure: 0.1.3 - jest-runner-groups: 2.2.0_izd2bb4ook7j3zolwvt7oz6lf4 + jest-runner-groups: 2.2.0_si6aka6xkbrcvhz7vk2qr7go64 postman-collection: 4.1.5 supertest: 6.2.4 devDependencies: @@ -800,7 +800,7 @@ importers: dependencies: '@jest/globals': 27.5.1 '@woocommerce/api': link:../api - '@woocommerce/e2e-utils': 0.1.6_drwlo4y73cla4rzwrrjk6eyf6e + '@woocommerce/e2e-utils': 0.1.6_lwdsu27pg5ztgtejufpr6ijesa '@wordpress/deprecated': 3.6.1 config: 3.3.7 devDependencies: @@ -920,7 +920,7 @@ importers: '@automattic/puppeteer-utils': github.com/Automattic/puppeteer-utils/0f3ec50_react-native@0.70.0 '@woocommerce/api': link:../api '@wordpress/deprecated': 3.6.1 - '@wordpress/e2e-test-utils': 7.2.1_hmvsd4hlimltsdqszkn2ou62si + '@wordpress/e2e-test-utils': 7.2.1_lbgvwpw4gzlmvdk3yuc5tcyftm config: 3.3.7 fishery: 1.4.0 devDependencies: @@ -2254,6 +2254,33 @@ importers: tslib: 2.3.1 typescript: 4.9.5 + tools/monorepo-utils: + specifiers: + '@commander-js/extra-typings': ^0.1.0 + '@types/jest': ^27.4.1 + '@types/node': ^16.18.18 + '@woocommerce/eslint-plugin': workspace:* + chalk: ^4.1.2 + commander: ^9.4.0 + dotenv: ^10.0.0 + eslint: ^8.32.0 + jest: ^29.5.0 + ts-jest: ^29.1.0 + typescript: ^4.9.5 + dependencies: + '@commander-js/extra-typings': 0.1.0_commander@9.4.0 + chalk: 4.1.2 + commander: 9.4.0 + dotenv: 10.0.0 + devDependencies: + '@types/jest': 27.4.1 + '@types/node': 16.18.21 + '@woocommerce/eslint-plugin': link:../../packages/js/eslint-plugin + eslint: 8.32.0 + jest: 29.5.0_@types+node@16.18.21 + ts-jest: 29.1.0_q744lk64cx4tbpg7zn4yr5nesi + typescript: 4.9.5 + tools/package-release: specifiers: '@oclif/core': ^1 @@ -2514,7 +2541,7 @@ packages: qs: 6.10.3 react: 17.0.2 redux: 4.2.0 - tslib: 2.3.1 + tslib: 2.5.0 utility-types: 3.10.0 validator: 13.7.0 transitivePeerDependencies: @@ -2563,13 +2590,13 @@ packages: dependencies: '@automattic/explat-client': 0.0.3 react: 17.0.2 - tslib: 2.3.1 + tslib: 2.5.0 dev: false /@automattic/explat-client/0.0.3: resolution: {integrity: sha512-N2/H9l3JZLZLSIyZJMnHKUWZWFjeakU40vm3k3EHdCHdKh8pu2Mz/BrMbtWImYBzaEJnbUZrHM/fAuhFy4sORg==} dependencies: - tslib: 2.3.1 + tslib: 2.5.0 dev: false /@automattic/format-currency/1.0.0-alpha.0: @@ -2980,6 +3007,32 @@ packages: browserslist: 4.19.3 semver: 6.3.0 + /@babel/helper-compilation-targets/7.17.7_@babel+core@7.12.9: + resolution: {integrity: sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.21.0 + '@babel/core': 7.12.9 + '@babel/helper-validator-option': 7.18.6 + browserslist: 4.21.4 + semver: 6.3.0 + dev: true + + /@babel/helper-compilation-targets/7.17.7_@babel+core@7.17.8: + resolution: {integrity: sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.21.0 + '@babel/core': 7.17.8 + '@babel/helper-validator-option': 7.18.6 + browserslist: 4.21.4 + semver: 6.3.0 + dev: true + /@babel/helper-compilation-targets/7.17.7_@babel+core@7.21.3: resolution: {integrity: sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w==} engines: {node: '>=6.9.0'} @@ -3045,6 +3098,24 @@ packages: lru-cache: 5.1.1 semver: 6.3.0 + /@babel/helper-create-class-features-plugin/7.17.6_@babel+core@7.12.9: + resolution: {integrity: sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-member-expression-to-functions': 7.21.0 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-replace-supers': 7.20.7 + '@babel/helper-split-export-declaration': 7.18.6 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/helper-create-class-features-plugin/7.17.6_@babel+core@7.17.8: resolution: {integrity: sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==} engines: {node: '>=6.9.0'} @@ -3569,6 +3640,26 @@ packages: dependencies: '@babel/types': 7.21.3 + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-anv/DObl7waiGEnC24O9zqL0pSuI9hljihqiDuFHC8d7/bjr/4RLGPWuc8rYOff/QPzbEPSkzG8wGG9aDuhHRg==} engines: {node: '>=6.9.0'} @@ -3608,6 +3699,30 @@ packages: '@babel/core': 7.21.3 '@babel/helper-plugin-utils': 7.20.2 + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 + '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.12.9 + dev: true + + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 + '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.17.8 + dev: true + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-di8vUHRdf+4aJ7ltXhaDbPoszdkh59AQtJM5soLsuHpQJdFQZOA4uGj0V2u/CZ8bJ/u8ULDL5yq6FO/bCXnKHw==} engines: {node: '>=6.9.0'} @@ -3668,6 +3783,34 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-proposal-async-generator-functions/7.16.8_@babel+core@7.12.9: + resolution: {integrity: sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.12.9 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.12.9 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-async-generator-functions/7.16.8_@babel+core@7.17.8: + resolution: {integrity: sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-remap-async-to-generator': 7.18.9_@babel+core@7.17.8 + '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.17.8 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-async-generator-functions/7.16.8_@babel+core@7.21.3: resolution: {integrity: sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==} engines: {node: '>=6.9.0'} @@ -3737,6 +3880,19 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-proposal-class-properties/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-create-class-features-plugin': 7.17.6_@babel+core@7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-class-properties/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==} engines: {node: '>=6.9.0'} @@ -3799,6 +3955,34 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-proposal-class-static-block/7.17.6_@babel+core@7.12.9: + resolution: {integrity: sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-create-class-features-plugin': 7.19.0_@babel+core@7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.12.9 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-proposal-class-static-block/7.17.6_@babel+core@7.17.8: + resolution: {integrity: sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-create-class-features-plugin': 7.19.0_@babel+core@7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.17.8 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-class-static-block/7.17.6_@babel+core@7.21.3: resolution: {integrity: sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==} engines: {node: '>=6.9.0'} @@ -3892,6 +4076,28 @@ packages: '@babel/helper-plugin-utils': 7.19.0 '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.12.9 + /@babel/plugin-proposal-dynamic-import/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.12.9 + dev: true + + /@babel/plugin-proposal-dynamic-import/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.17.8 + dev: true + /@babel/plugin-proposal-dynamic-import/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==} engines: {node: '>=6.9.0'} @@ -3976,6 +4182,28 @@ packages: '@babel/helper-plugin-utils': 7.19.0 '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.12.9 + /@babel/plugin-proposal-export-namespace-from/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.12.9 + dev: true + + /@babel/plugin-proposal-export-namespace-from/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-export-namespace-from': 7.8.3_@babel+core@7.17.8 + dev: true + /@babel/plugin-proposal-export-namespace-from/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==} engines: {node: '>=6.9.0'} @@ -4029,6 +4257,28 @@ packages: '@babel/helper-plugin-utils': 7.19.0 '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.12.9 + /@babel/plugin-proposal-json-strings/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.12.9 + dev: true + + /@babel/plugin-proposal-json-strings/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.17.8 + dev: true + /@babel/plugin-proposal-json-strings/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==} engines: {node: '>=6.9.0'} @@ -4082,6 +4332,28 @@ packages: '@babel/helper-plugin-utils': 7.19.0 '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.12.9 + /@babel/plugin-proposal-logical-assignment-operators/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.12.9 + dev: true + + /@babel/plugin-proposal-logical-assignment-operators/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.17.8 + dev: true + /@babel/plugin-proposal-logical-assignment-operators/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==} engines: {node: '>=6.9.0'} @@ -4135,6 +4407,17 @@ packages: '@babel/helper-plugin-utils': 7.19.0 '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.12.9 + /@babel/plugin-proposal-nullish-coalescing-operator/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.12.9 + dev: true + /@babel/plugin-proposal-nullish-coalescing-operator/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==} engines: {node: '>=6.9.0'} @@ -4197,6 +4480,28 @@ packages: '@babel/helper-plugin-utils': 7.19.0 '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.12.9 + /@babel/plugin-proposal-numeric-separator/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.12.9 + dev: true + + /@babel/plugin-proposal-numeric-separator/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.17.8 + dev: true + /@babel/plugin-proposal-numeric-separator/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==} engines: {node: '>=6.9.0'} @@ -4264,6 +4569,20 @@ packages: '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.12.9 '@babel/plugin-transform-parameters': 7.16.3_@babel+core@7.12.9 + /@babel/plugin-proposal-object-rest-spread/7.17.3_@babel+core@7.12.9: + resolution: {integrity: sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.21.0 + '@babel/core': 7.12.9 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.12.9 + '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.12.9 + dev: true + /@babel/plugin-proposal-object-rest-spread/7.17.3_@babel+core@7.17.8: resolution: {integrity: sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw==} engines: {node: '>=6.9.0'} @@ -4342,6 +4661,28 @@ packages: '@babel/helper-plugin-utils': 7.19.0 '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.12.9 + /@babel/plugin-proposal-optional-catch-binding/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.12.9 + dev: true + + /@babel/plugin-proposal-optional-catch-binding/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.17.8 + dev: true + /@babel/plugin-proposal-optional-catch-binding/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==} engines: {node: '>=6.9.0'} @@ -4395,6 +4736,18 @@ packages: '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.12.9 + /@babel/plugin-proposal-optional-chaining/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 + '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.12.9 + dev: true + /@babel/plugin-proposal-optional-chaining/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==} engines: {node: '>=6.9.0'} @@ -4464,6 +4817,19 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-proposal-private-methods/7.16.11_@babel+core@7.12.9: + resolution: {integrity: sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-create-class-features-plugin': 7.17.6_@babel+core@7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-private-methods/7.16.11_@babel+core@7.17.8: resolution: {integrity: sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==} engines: {node: '>=6.9.0'} @@ -4528,6 +4894,21 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-proposal-private-property-in-object/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-annotate-as-pure': 7.16.7 + '@babel/helper-create-class-features-plugin': 7.17.6_@babel+core@7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.12.9 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-proposal-private-property-in-object/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-rMQkjcOFbm+ufe3bTZLyOfsOUOxyvLXZJCTARhJr+8UMSoZmqTe1K1BgkFcrW37rAchWg57yI69ORxiWvUINuQ==} engines: {node: '>=6.9.0'} @@ -4612,6 +4993,28 @@ packages: '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-proposal-unicode-property-regex/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==} + engines: {node: '>=4'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-proposal-unicode-property-regex/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==} + engines: {node: '>=4'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-proposal-unicode-property-regex/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==} engines: {node: '>=4'} @@ -5270,6 +5673,16 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-arrow-functions/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-arrow-functions/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==} engines: {node: '>=6.9.0'} @@ -5331,6 +5744,20 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-transform-async-to-generator/7.16.8_@babel+core@7.12.9: + resolution: {integrity: sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-module-imports': 7.16.7 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-remap-async-to-generator': 7.16.8 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-async-to-generator/7.16.8_@babel+core@7.17.8: resolution: {integrity: sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==} engines: {node: '>=6.9.0'} @@ -5408,6 +5835,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-block-scoped-functions/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-block-scoped-functions/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-block-scoped-functions/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==} engines: {node: '>=6.9.0'} @@ -5455,6 +5902,16 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-block-scoping/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-block-scoping/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==} engines: {node: '>=6.9.0'} @@ -5520,6 +5977,25 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-transform-classes/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-annotate-as-pure': 7.18.6 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-optimise-call-expression': 7.18.6 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-replace-supers': 7.19.1 + '@babel/helper-split-export-declaration': 7.18.6 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-classes/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==} engines: {node: '>=6.9.0'} @@ -5625,6 +6101,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-computed-properties/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-computed-properties/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-computed-properties/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==} engines: {node: '>=6.9.0'} @@ -5672,6 +6168,16 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-destructuring/7.17.7_@babel+core@7.12.9: + resolution: {integrity: sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-destructuring/7.17.7_@babel+core@7.17.8: resolution: {integrity: sha512-XVh0r5yq9sLR4vZ6eVZe8FKfIcSgaTBxVBRSYokRj2qksf6QerYnTxz9/GTuKTH/n/HwLP7t6gtlybHetJ/6hQ==} engines: {node: '>=6.9.0'} @@ -5730,6 +6236,28 @@ packages: '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-dotall-regex/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-dotall-regex/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-dotall-regex/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==} engines: {node: '>=6.9.0'} @@ -5781,6 +6309,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-duplicate-keys/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-duplicate-keys/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-duplicate-keys/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==} engines: {node: '>=6.9.0'} @@ -5830,6 +6378,28 @@ packages: '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-exponentiation-operator/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-exponentiation-operator/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-exponentiation-operator/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==} engines: {node: '>=6.9.0'} @@ -5911,6 +6481,16 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-for-of/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-for-of/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==} engines: {node: '>=6.9.0'} @@ -5969,6 +6549,30 @@ packages: '@babel/helper-function-name': 7.19.0 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-function-name/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.12.9 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-function-name/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.17.8 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-function-name/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==} engines: {node: '>=6.9.0'} @@ -6024,6 +6628,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-literals/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-literals/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-literals/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==} engines: {node: '>=6.9.0'} @@ -6071,6 +6695,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-member-expression-literals/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-member-expression-literals/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-member-expression-literals/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==} engines: {node: '>=6.9.0'} @@ -6122,6 +6766,34 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-transform-modules-amd/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-amd/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-modules-amd/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==} engines: {node: '>=6.9.0'} @@ -6188,6 +6860,21 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-transform-modules-commonjs/7.17.7_@babel+core@7.12.9: + resolution: {integrity: sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-simple-access': 7.18.6 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-modules-commonjs/7.17.7_@babel+core@7.17.8: resolution: {integrity: sha512-ITPmR2V7MqioMJyrxUo2onHNC3e+MvfFiFIR0RP21d3PtlVb6sfzoxNKiphSZUOM9hEIdzCcZe83ieX3yoqjUA==} engines: {node: '>=6.9.0'} @@ -6272,6 +6959,38 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-transform-modules-systemjs/7.17.8_@babel+core@7.12.9: + resolution: {integrity: sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-validator-identifier': 7.19.1 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-systemjs/7.17.8_@babel+core@7.17.8: + resolution: {integrity: sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-validator-identifier': 7.19.1 + babel-plugin-dynamic-import-node: 2.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-modules-systemjs/7.17.8_@babel+core@7.21.3: resolution: {integrity: sha512-39reIkMTUVagzgA5x88zDYXPCMT6lcaRKs1+S9K6NKBPErbgO/w/kP8GlNQTC87b412ZTlmNgr3k2JrWgHH+Bw==} engines: {node: '>=6.9.0'} @@ -6344,6 +7063,32 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-transform-modules-umd/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-modules-umd/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-module-transforms': 7.21.2 + '@babel/helper-plugin-utils': 7.20.2 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-modules-umd/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==} engines: {node: '>=6.9.0'} @@ -6404,6 +7149,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.12.9 + /@babel/plugin-transform-named-capturing-groups-regex/7.16.8_@babel+core@7.12.9: + resolution: {integrity: sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.12.9 + dev: true + + /@babel/plugin-transform-named-capturing-groups-regex/7.16.8_@babel+core@7.17.8: + resolution: {integrity: sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.17.8 + dev: true + /@babel/plugin-transform-named-capturing-groups-regex/7.16.8_@babel+core@7.21.3: resolution: {integrity: sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==} engines: {node: '>=6.9.0'} @@ -6454,6 +7219,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-new-target/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-new-target/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-new-target/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==} engines: {node: '>=6.9.0'} @@ -6505,6 +7290,32 @@ packages: transitivePeerDependencies: - supports-color + /@babel/plugin-transform-object-super/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-replace-supers': 7.19.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/plugin-transform-object-super/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-replace-supers': 7.19.1 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/plugin-transform-object-super/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==} engines: {node: '>=6.9.0'} @@ -6564,6 +7375,16 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-parameters/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-parameters/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==} engines: {node: '>=6.9.0'} @@ -6621,6 +7442,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-property-literals/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-property-literals/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-property-literals/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==} engines: {node: '>=6.9.0'} @@ -6866,6 +7707,26 @@ packages: '@babel/core': 7.12.9 regenerator-transform: 0.14.5 + /@babel/plugin-transform-regenerator/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + regenerator-transform: 0.14.5 + dev: true + + /@babel/plugin-transform-regenerator/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + regenerator-transform: 0.14.5 + dev: true + /@babel/plugin-transform-regenerator/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==} engines: {node: '>=6.9.0'} @@ -6917,6 +7778,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-reserved-words/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-reserved-words/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-reserved-words/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==} engines: {node: '>=6.9.0'} @@ -7031,6 +7912,16 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-shorthand-properties/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-shorthand-properties/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==} engines: {node: '>=6.9.0'} @@ -7089,6 +7980,17 @@ packages: '@babel/helper-plugin-utils': 7.19.0 '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 + /@babel/plugin-transform-spread/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-skip-transparent-expression-wrappers': 7.18.9 + dev: true + /@babel/plugin-transform-spread/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==} engines: {node: '>=6.9.0'} @@ -7151,6 +8053,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-sticky-regex/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-sticky-regex/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-sticky-regex/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==} engines: {node: '>=6.9.0'} @@ -7198,6 +8120,16 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-template-literals/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-template-literals/7.16.7_@babel+core@7.17.8: resolution: {integrity: sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==} engines: {node: '>=6.9.0'} @@ -7255,6 +8187,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-typeof-symbol/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-typeof-symbol/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-typeof-symbol/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==} engines: {node: '>=6.9.0'} @@ -7355,6 +8307,26 @@ packages: '@babel/core': 7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-unicode-escapes/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-unicode-escapes/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-unicode-escapes/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==} engines: {node: '>=6.9.0'} @@ -7404,6 +8376,28 @@ packages: '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.12.9 '@babel/helper-plugin-utils': 7.19.0 + /@babel/plugin-transform-unicode-regex/7.16.7_@babel+core@7.12.9: + resolution: {integrity: sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.12.9 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.12.9 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-transform-unicode-regex/7.16.7_@babel+core@7.17.8: + resolution: {integrity: sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.17.8 + '@babel/helper-create-regexp-features-plugin': 7.19.0_@babel+core@7.17.8 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + /@babel/plugin-transform-unicode-regex/7.16.7_@babel+core@7.21.3: resolution: {integrity: sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==} engines: {node: '>=6.9.0'} @@ -7535,28 +8529,28 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.0 + '@babel/compat-data': 7.17.7 '@babel/core': 7.12.9 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.12.9 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.18.6 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-proposal-async-generator-functions': 7.20.7_@babel+core@7.12.9 - '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-proposal-class-static-block': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-proposal-export-namespace-from': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-proposal-json-strings': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-proposal-logical-assignment-operators': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.12.9 - '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-proposal-private-property-in-object': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.12.9 + '@babel/helper-compilation-targets': 7.17.7_@babel+core@7.12.9 + '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-validator-option': 7.16.7 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-proposal-async-generator-functions': 7.16.8_@babel+core@7.12.9 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-proposal-class-static-block': 7.17.6_@babel+core@7.12.9 + '@babel/plugin-proposal-dynamic-import': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-proposal-export-namespace-from': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-proposal-json-strings': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-proposal-logical-assignment-operators': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-proposal-numeric-separator': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-proposal-object-rest-spread': 7.17.3_@babel+core@7.12.9 + '@babel/plugin-proposal-optional-catch-binding': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-proposal-private-methods': 7.16.11_@babel+core@7.12.9 + '@babel/plugin-proposal-private-property-in-object': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-proposal-unicode-property-regex': 7.16.7_@babel+core@7.12.9 '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.12.9 '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.12.9 '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.12.9 @@ -7571,44 +8565,44 @@ packages: '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.12.9 '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.12.9 '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.12.9 - '@babel/plugin-transform-arrow-functions': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-async-to-generator': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-block-scoping': 7.21.0_@babel+core@7.12.9 - '@babel/plugin-transform-classes': 7.21.0_@babel+core@7.12.9 - '@babel/plugin-transform-computed-properties': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-transform-destructuring': 7.21.3_@babel+core@7.12.9 - '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-for-of': 7.18.8_@babel+core@7.12.9 - '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-modules-amd': 7.20.11_@babel+core@7.12.9 - '@babel/plugin-transform-modules-commonjs': 7.21.2_@babel+core@7.12.9 - '@babel/plugin-transform-modules-systemjs': 7.20.11_@babel+core@7.12.9 - '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-named-capturing-groups-regex': 7.19.1_@babel+core@7.12.9 - '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.12.9 - '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-regenerator': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-spread': 7.19.0_@babel+core@7.12.9 - '@babel/plugin-transform-sticky-regex': 7.18.6_@babel+core@7.12.9 - '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-transform-typeof-symbol': 7.18.9_@babel+core@7.12.9 - '@babel/plugin-transform-unicode-escapes': 7.18.10_@babel+core@7.12.9 - '@babel/plugin-transform-unicode-regex': 7.18.6_@babel+core@7.12.9 + '@babel/plugin-transform-arrow-functions': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-async-to-generator': 7.16.8_@babel+core@7.12.9 + '@babel/plugin-transform-block-scoped-functions': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-block-scoping': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-classes': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-computed-properties': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-destructuring': 7.17.7_@babel+core@7.12.9 + '@babel/plugin-transform-dotall-regex': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-duplicate-keys': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-exponentiation-operator': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-for-of': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-function-name': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-literals': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-member-expression-literals': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-modules-amd': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-modules-commonjs': 7.17.7_@babel+core@7.12.9 + '@babel/plugin-transform-modules-systemjs': 7.17.8_@babel+core@7.12.9 + '@babel/plugin-transform-modules-umd': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-named-capturing-groups-regex': 7.16.8_@babel+core@7.12.9 + '@babel/plugin-transform-new-target': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-object-super': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-property-literals': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-regenerator': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-reserved-words': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-shorthand-properties': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-spread': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-sticky-regex': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-template-literals': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-typeof-symbol': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-unicode-escapes': 7.16.7_@babel+core@7.12.9 + '@babel/plugin-transform-unicode-regex': 7.16.7_@babel+core@7.12.9 '@babel/preset-modules': 0.1.5_@babel+core@7.12.9 - '@babel/types': 7.21.3 - babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.12.9 + '@babel/types': 7.17.0 + babel-plugin-polyfill-corejs2: 0.3.0_@babel+core@7.12.9 babel-plugin-polyfill-corejs3: 0.5.2_@babel+core@7.12.9 babel-plugin-polyfill-regenerator: 0.3.0_@babel+core@7.12.9 - core-js-compat: 3.25.5 + core-js-compat: 3.21.1 semver: 6.3.0 transitivePeerDependencies: - supports-color @@ -7620,28 +8614,28 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.21.0 + '@babel/compat-data': 7.17.7 '@babel/core': 7.17.8 - '@babel/helper-compilation-targets': 7.20.7_@babel+core@7.17.8 - '@babel/helper-plugin-utils': 7.20.2 - '@babel/helper-validator-option': 7.18.6 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-proposal-async-generator-functions': 7.20.7_@babel+core@7.17.8 - '@babel/plugin-proposal-class-properties': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-proposal-class-static-block': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-proposal-dynamic-import': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-proposal-export-namespace-from': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-proposal-json-strings': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-proposal-logical-assignment-operators': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-proposal-numeric-separator': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-proposal-object-rest-spread': 7.20.7_@babel+core@7.17.8 - '@babel/plugin-proposal-optional-catch-binding': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-proposal-optional-chaining': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-proposal-private-methods': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-proposal-private-property-in-object': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-proposal-unicode-property-regex': 7.18.6_@babel+core@7.17.8 + '@babel/helper-compilation-targets': 7.17.7_@babel+core@7.17.8 + '@babel/helper-plugin-utils': 7.18.9 + '@babel/helper-validator-option': 7.16.7 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-proposal-async-generator-functions': 7.16.8_@babel+core@7.17.8 + '@babel/plugin-proposal-class-properties': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-proposal-class-static-block': 7.17.6_@babel+core@7.17.8 + '@babel/plugin-proposal-dynamic-import': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-proposal-export-namespace-from': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-proposal-json-strings': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-proposal-logical-assignment-operators': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-proposal-numeric-separator': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-proposal-object-rest-spread': 7.17.3_@babel+core@7.17.8 + '@babel/plugin-proposal-optional-catch-binding': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-proposal-optional-chaining': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-proposal-private-methods': 7.16.11_@babel+core@7.17.8 + '@babel/plugin-proposal-private-property-in-object': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-proposal-unicode-property-regex': 7.16.7_@babel+core@7.17.8 '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.17.8 '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.17.8 '@babel/plugin-syntax-class-static-block': 7.14.5_@babel+core@7.17.8 @@ -7656,44 +8650,44 @@ packages: '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.17.8 '@babel/plugin-syntax-private-property-in-object': 7.14.5_@babel+core@7.17.8 '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.17.8 - '@babel/plugin-transform-arrow-functions': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-async-to-generator': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-block-scoped-functions': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-block-scoping': 7.21.0_@babel+core@7.17.8 - '@babel/plugin-transform-classes': 7.21.0_@babel+core@7.17.8 - '@babel/plugin-transform-computed-properties': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-transform-destructuring': 7.21.3_@babel+core@7.17.8 - '@babel/plugin-transform-dotall-regex': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-duplicate-keys': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-transform-exponentiation-operator': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-for-of': 7.18.8_@babel+core@7.17.8 - '@babel/plugin-transform-function-name': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-transform-literals': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-transform-member-expression-literals': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-modules-amd': 7.20.11_@babel+core@7.17.8 - '@babel/plugin-transform-modules-commonjs': 7.21.2_@babel+core@7.17.8 - '@babel/plugin-transform-modules-systemjs': 7.20.11_@babel+core@7.17.8 - '@babel/plugin-transform-modules-umd': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-named-capturing-groups-regex': 7.19.1_@babel+core@7.17.8 - '@babel/plugin-transform-new-target': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-object-super': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-parameters': 7.21.3_@babel+core@7.17.8 - '@babel/plugin-transform-property-literals': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-regenerator': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-reserved-words': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-shorthand-properties': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-spread': 7.19.0_@babel+core@7.17.8 - '@babel/plugin-transform-sticky-regex': 7.18.6_@babel+core@7.17.8 - '@babel/plugin-transform-template-literals': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-transform-typeof-symbol': 7.18.9_@babel+core@7.17.8 - '@babel/plugin-transform-unicode-escapes': 7.18.10_@babel+core@7.17.8 - '@babel/plugin-transform-unicode-regex': 7.18.6_@babel+core@7.17.8 + '@babel/plugin-transform-arrow-functions': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-async-to-generator': 7.16.8_@babel+core@7.17.8 + '@babel/plugin-transform-block-scoped-functions': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-block-scoping': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-classes': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-computed-properties': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-destructuring': 7.17.7_@babel+core@7.17.8 + '@babel/plugin-transform-dotall-regex': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-duplicate-keys': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-exponentiation-operator': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-for-of': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-function-name': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-literals': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-member-expression-literals': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-modules-amd': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-modules-commonjs': 7.17.7_@babel+core@7.17.8 + '@babel/plugin-transform-modules-systemjs': 7.17.8_@babel+core@7.17.8 + '@babel/plugin-transform-modules-umd': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-named-capturing-groups-regex': 7.16.8_@babel+core@7.17.8 + '@babel/plugin-transform-new-target': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-object-super': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-parameters': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-property-literals': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-regenerator': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-reserved-words': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-shorthand-properties': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-spread': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-sticky-regex': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-template-literals': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-typeof-symbol': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-unicode-escapes': 7.16.7_@babel+core@7.17.8 + '@babel/plugin-transform-unicode-regex': 7.16.7_@babel+core@7.17.8 '@babel/preset-modules': 0.1.5_@babel+core@7.17.8 - '@babel/types': 7.21.3 - babel-plugin-polyfill-corejs2: 0.3.3_@babel+core@7.17.8 + '@babel/types': 7.17.0 + babel-plugin-polyfill-corejs2: 0.3.0_@babel+core@7.17.8 babel-plugin-polyfill-corejs3: 0.5.2_@babel+core@7.17.8 babel-plugin-polyfill-regenerator: 0.3.0_@babel+core@7.17.8 - core-js-compat: 3.25.5 + core-js-compat: 3.21.1 semver: 6.3.0 transitivePeerDependencies: - supports-color @@ -9017,6 +10011,17 @@ packages: jest-util: 27.5.1 slash: 3.0.0 + /@jest/console/29.5.0: + resolution: {integrity: sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@types/node': 16.18.21 + chalk: 4.1.2 + jest-message-util: 29.5.0 + jest-util: 29.5.0 + slash: 3.0.0 + /@jest/core/24.9.0: resolution: {integrity: sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==} engines: {node: '>= 6'} @@ -9178,6 +10183,47 @@ packages: - ts-node - utf-8-validate + /@jest/core/29.5.0: + resolution: {integrity: sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.5.0 + '@jest/reporters': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 16.18.21 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.2.0 + exit: 0.1.2 + graceful-fs: 4.2.9 + jest-changed-files: 29.5.0 + jest-config: 29.5.0_@types+node@16.18.21 + jest-haste-map: 29.5.0 + jest-message-util: 29.5.0 + jest-regex-util: 29.4.3 + jest-resolve: 29.5.0 + jest-resolve-dependencies: 29.5.0 + jest-runner: 29.5.0 + jest-runtime: 29.5.0 + jest-snapshot: 29.5.0 + jest-util: 29.5.0 + jest-validate: 29.5.0 + jest-watcher: 29.5.0 + micromatch: 4.0.5 + pretty-format: 29.5.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - supports-color + - ts-node + /@jest/create-cache-key-function/27.5.1: resolution: {integrity: sha512-dmH1yW+makpTSURTy8VzdUwFnfQh1G8R+DxO2Ho2FFmBbKFEVm+3jWdvFhE2VqB/LATCTokkP0dotjyQyw5/AQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -9224,6 +10270,30 @@ packages: '@types/node': 16.18.21 jest-mock: 27.5.1 + /@jest/environment/29.5.0: + resolution: {integrity: sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 16.18.21 + jest-mock: 29.5.0 + + /@jest/expect-utils/29.5.0: + resolution: {integrity: sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.4.3 + + /@jest/expect/29.5.0: + resolution: {integrity: sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.5.0 + jest-snapshot: 29.5.0 + transitivePeerDependencies: + - supports-color + /@jest/fake-timers/24.9.0: resolution: {integrity: sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==} engines: {node: '>= 6'} @@ -9269,6 +10339,17 @@ packages: jest-mock: 27.5.1 jest-util: 27.5.1 + /@jest/fake-timers/29.5.0: + resolution: {integrity: sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@sinonjs/fake-timers': 10.0.2 + '@types/node': 16.18.21 + jest-message-util: 29.5.0 + jest-mock: 29.5.0 + jest-util: 29.5.0 + /@jest/globals/25.5.2: resolution: {integrity: sha512-AgAS/Ny7Q2RCIj5kZ+0MuKM1wbF0WMLxbCVl/GOMoCNbODRdJ541IxJ98xnZdVSZXivKpJlNPIWa3QmY0l4CXA==} engines: {node: '>= 8.3'} @@ -9295,6 +10376,17 @@ packages: '@jest/types': 27.5.1 expect: 27.5.1 + /@jest/globals/29.5.0: + resolution: {integrity: sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/expect': 29.5.0 + '@jest/types': 29.5.0 + jest-mock: 29.5.0 + transitivePeerDependencies: + - supports-color + /@jest/reporters/24.9.0: resolution: {integrity: sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==} engines: {node: '>= 6'} @@ -9431,6 +10523,48 @@ packages: transitivePeerDependencies: - supports-color + /@jest/reporters/29.5.0: + resolution: {integrity: sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@jridgewell/trace-mapping': 0.3.17 + '@types/node': 16.18.21 + chalk: 4.1.2 + collect-v8-coverage: 1.0.1 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.9 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-instrument: 5.1.0 + istanbul-lib-report: 3.0.0 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.4 + jest-message-util: 29.5.0 + jest-util: 29.5.0 + jest-worker: 29.5.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.1.0 + transitivePeerDependencies: + - supports-color + + /@jest/schemas/29.4.3: + resolution: {integrity: sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.25.24 + /@jest/source-map/24.9.0: resolution: {integrity: sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==} engines: {node: '>= 6'} @@ -9466,6 +10600,14 @@ packages: graceful-fs: 4.2.9 source-map: 0.6.1 + /@jest/source-map/29.4.3: + resolution: {integrity: sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.17 + callsites: 3.1.0 + graceful-fs: 4.2.9 + /@jest/test-result/24.9.0: resolution: {integrity: sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==} engines: {node: '>= 6'} @@ -9504,6 +10646,15 @@ packages: '@types/istanbul-lib-coverage': 2.0.3 collect-v8-coverage: 1.0.1 + /@jest/test-result/29.5.0: + resolution: {integrity: sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.5.0 + '@jest/types': 29.5.0 + '@types/istanbul-lib-coverage': 2.0.3 + collect-v8-coverage: 1.0.1 + /@jest/test-sequencer/24.9.0: resolution: {integrity: sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==} engines: {node: '>= 6'} @@ -9562,6 +10713,15 @@ packages: transitivePeerDependencies: - supports-color + /@jest/test-sequencer/29.5.0: + resolution: {integrity: sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.5.0 + graceful-fs: 4.2.9 + jest-haste-map: 29.5.0 + slash: 3.0.0 + /@jest/transform/24.9.0: resolution: {integrity: sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==} engines: {node: '>= 6'} @@ -9654,6 +10814,28 @@ packages: transitivePeerDependencies: - supports-color + /@jest/transform/29.5.0: + resolution: {integrity: sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.21.3 + '@jest/types': 29.5.0 + '@jridgewell/trace-mapping': 0.3.17 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.9 + jest-haste-map: 29.5.0 + jest-regex-util: 29.4.3 + jest-util: 29.5.0 + micromatch: 4.0.5 + pirates: 4.0.5 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + /@jest/types/24.9.0: resolution: {integrity: sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==} engines: {node: '>= 6'} @@ -9693,6 +10875,17 @@ packages: '@types/yargs': 16.0.4 chalk: 4.1.2 + /@jest/types/29.5.0: + resolution: {integrity: sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.4.3 + '@types/istanbul-lib-coverage': 2.0.3 + '@types/istanbul-reports': 3.0.1 + '@types/node': 16.18.21 + '@types/yargs': 17.0.24 + chalk: 4.1.2 + /@jridgewell/gen-mapping/0.1.1: resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} engines: {node: '>=6.0.0'} @@ -10052,7 +11245,7 @@ packages: strip-ansi: 6.0.1 supports-color: 8.1.1 supports-hyperlinks: 2.2.0 - tslib: 2.3.1 + tslib: 2.5.0 widest-line: 3.1.0 wrap-ansi: 7.0.0 @@ -10122,7 +11315,7 @@ packages: load-json-file: 5.3.0 npm-run-path: 4.0.1 semver: 7.3.7 - tslib: 2.3.1 + tslib: 2.5.0 yarn: 1.22.19 transitivePeerDependencies: - supports-color @@ -10766,6 +11959,9 @@ packages: /@sideway/pinpoint/2.0.0: resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} + /@sinclair/typebox/0.25.24: + resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==} + /@sindresorhus/is/2.1.1: resolution: {integrity: sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==} engines: {node: '>=10'} @@ -10781,6 +11977,16 @@ packages: dependencies: type-detect: 4.0.8 + /@sinonjs/commons/2.0.0: + resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==} + dependencies: + type-detect: 4.0.8 + + /@sinonjs/fake-timers/10.0.2: + resolution: {integrity: sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==} + dependencies: + '@sinonjs/commons': 2.0.0 + /@sinonjs/fake-timers/6.0.1: resolution: {integrity: sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==} dependencies: @@ -14863,6 +16069,11 @@ packages: dependencies: '@types/yargs-parser': 20.2.1 + /@types/yargs/17.0.24: + resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} + dependencies: + '@types/yargs-parser': 20.2.1 + /@types/yauzl/2.9.2: resolution: {integrity: sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==} requiresBuild: true @@ -15537,14 +16748,14 @@ packages: prop-types: 15.8.1 react: 17.0.2 - /@woocommerce/e2e-utils/0.1.6_drwlo4y73cla4rzwrrjk6eyf6e: + /@woocommerce/e2e-utils/0.1.6_lwdsu27pg5ztgtejufpr6ijesa: resolution: {integrity: sha512-gWSEgFIjMqaqiiIyrpa1epIHkmBBAfk6WfRojva1f5ZmffSJCc0VbX2jQQRdFm1BuEYr8KGCCYo+q8NIjlMZ7g==} peerDependencies: '@woocommerce/api': ^0.2.0 dependencies: '@woocommerce/api': link:packages/js/api '@wordpress/deprecated': 2.12.3 - '@wordpress/e2e-test-utils': 4.16.1_eod7vs2qyqnfu2oldnxglnszkq + '@wordpress/e2e-test-utils': 4.16.1_d2bihccpwwyuin5554pd3vha7i config: 3.3.3 faker: 5.5.3 fishery: 1.4.0 @@ -17241,6 +18452,25 @@ packages: - react-native dev: false + /@wordpress/e2e-test-utils/4.16.1_d2bihccpwwyuin5554pd3vha7i: + resolution: {integrity: sha512-Dpsq5m0VSvjIhro2MjACSzkOkOf1jGEryzgEMW1ikbT6YI+motspHfGtisKXgYhZJOnjV4PwuEg+9lPVnd971g==} + engines: {node: '>=8'} + peerDependencies: + jest: '>=24' + puppeteer: '>=1.19.0' + dependencies: + '@babel/runtime': 7.21.0 + '@wordpress/keycodes': 2.19.3 + '@wordpress/url': 2.22.2_react-native@0.70.0 + jest: 29.5.0 + lodash: 4.17.21 + node-fetch: 2.6.7 + puppeteer: 2.1.1 + transitivePeerDependencies: + - encoding + - react-native + dev: false + /@wordpress/e2e-test-utils/4.16.1_eod7vs2qyqnfu2oldnxglnszkq: resolution: {integrity: sha512-Dpsq5m0VSvjIhro2MjACSzkOkOf1jGEryzgEMW1ikbT6YI+motspHfGtisKXgYhZJOnjV4PwuEg+9lPVnd971g==} engines: {node: '>=8'} @@ -17260,7 +18490,7 @@ packages: - react-native dev: false - /@wordpress/e2e-test-utils/7.2.1_hmvsd4hlimltsdqszkn2ou62si: + /@wordpress/e2e-test-utils/7.2.1_lbgvwpw4gzlmvdk3yuc5tcyftm: resolution: {integrity: sha512-eqo4quzgGFArZLLocC5sPfdCLiiS9zMTCRs+Kn4Tl4lLDOLhTk9I1MZcBkvJJ0Qd75FEcdeLaqNaSYNCu6rgyw==} engines: {node: '>=12'} peerDependencies: @@ -17272,7 +18502,7 @@ packages: '@wordpress/keycodes': 3.28.0 '@wordpress/url': 3.29.0 form-data: 4.0.0 - jest: 27.5.1 + jest: 29.5.0 lodash: 4.17.21 node-fetch: 2.6.7 puppeteer-core: 19.7.3_typescript@4.9.5 @@ -19763,6 +20993,23 @@ packages: transitivePeerDependencies: - supports-color + /babel-jest/29.5.0_@babel+core@7.21.3: + resolution: {integrity: sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.21.3 + '@jest/transform': 29.5.0 + '@types/babel__core': 7.1.16 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.5.0_@babel+core@7.21.3 + chalk: 4.1.2 + graceful-fs: 4.2.9 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + /babel-loader/8.2.3_7kihywspc3gmje7ccze4zrmvoq: resolution: {integrity: sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==} engines: {node: '>= 8.9'} @@ -19942,6 +21189,15 @@ packages: '@types/babel__core': 7.1.16 '@types/babel__traverse': 7.14.2 + /babel-plugin-jest-hoist/29.5.0: + resolution: {integrity: sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.20.7 + '@babel/types': 7.21.3 + '@types/babel__core': 7.1.16 + '@types/babel__traverse': 7.14.2 + /babel-plugin-macros/2.8.0: resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==} dependencies: @@ -19987,6 +21243,19 @@ packages: - supports-color dev: true + /babel-plugin-polyfill-corejs2/0.3.0_@babel+core@7.17.8: + resolution: {integrity: sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/compat-data': 7.21.0 + '@babel/core': 7.17.8 + '@babel/helper-define-polyfill-provider': 0.3.3_@babel+core@7.17.8 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + /babel-plugin-polyfill-corejs2/0.3.0_@babel+core@7.21.3: resolution: {integrity: sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA==} peerDependencies: @@ -20502,6 +21771,16 @@ packages: babel-plugin-jest-hoist: 27.5.1 babel-preset-current-node-syntax: 1.0.1_@babel+core@7.21.3 + /babel-preset-jest/29.5.0_@babel+core@7.21.3: + resolution: {integrity: sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.3 + babel-plugin-jest-hoist: 29.5.0 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.21.3 + /babel-runtime/6.26.0: resolution: {integrity: sha1-llxwWGaOgrVde/4E/yM3vItWR/4=} dependencies: @@ -20651,7 +21930,7 @@ packages: npm-normalize-package-bin: 2.0.0 read-cmd-shim: 3.0.1 rimraf: 3.0.2 - write-file-atomic: 4.0.1 + write-file-atomic: 4.0.2 dev: true /binary-extensions/1.13.1: @@ -22219,6 +23498,9 @@ packages: dependencies: safe-buffer: 5.1.2 + /convert-source-map/2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + /cookie-signature/1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} @@ -23422,6 +24704,10 @@ packages: resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /diff-sequences/29.4.3: + resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /diff/3.5.0: resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} engines: {node: '>=0.3.1'} @@ -23715,6 +25001,10 @@ packages: minimalistic-crypto-utils: 1.0.1 dev: true + /emittery/0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + /emittery/0.7.2: resolution: {integrity: sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==} engines: {node: '>=10'} @@ -25370,6 +26660,16 @@ packages: jest-matcher-utils: 27.5.1 jest-message-util: 27.5.1 + /expect/29.5.0: + resolution: {integrity: sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.5.0 + jest-get-type: 29.4.3 + jest-matcher-utils: 29.5.0 + jest-message-util: 29.5.0 + jest-util: 29.5.0 + /expose-loader/3.1.0_webpack@5.70.0: resolution: {integrity: sha512-2RExSo0yJiqP+xiUue13jQa2IHE8kLDzTI7b6kn+vUlBVvlzNSiLDzo4e5Pp5J039usvTUnxZ8sUOhv0Kg15NA==} engines: {node: '>= 12.13.0'} @@ -29098,6 +30398,13 @@ packages: execa: 5.1.1 throat: 6.0.1 + /jest-changed-files/29.5.0: + resolution: {integrity: sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + p-limit: 3.1.0 + /jest-circus/26.6.3: resolution: {integrity: sha512-ACrpWZGcQMpbv13XbzRzpytEJlilP/Su0JtNCi5r/xLpOUhnaIJr8leYYpLEMgPFURZISEHrnnpmB54Q/UziPw==} engines: {node: '>= 10.14.2'} @@ -29157,6 +30464,33 @@ packages: transitivePeerDependencies: - supports-color + /jest-circus/29.5.0: + resolution: {integrity: sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/expect': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 16.18.21 + chalk: 4.1.2 + co: 4.6.0 + dedent: 0.7.0 + is-generator-fn: 2.1.0 + jest-each: 29.5.0 + jest-matcher-utils: 29.5.0 + jest-message-util: 29.5.0 + jest-runtime: 29.5.0 + jest-snapshot: 29.5.0 + jest-util: 29.5.0 + p-limit: 3.1.0 + pretty-format: 29.5.0 + pure-rand: 6.0.1 + slash: 3.0.0 + stack-utils: 2.0.5 + transitivePeerDependencies: + - supports-color + /jest-cli/24.9.0: resolution: {integrity: sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==} engines: {node: '>= 6'} @@ -29262,6 +30596,62 @@ packages: - ts-node - utf-8-validate + /jest-cli/29.5.0: + resolution: {integrity: sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/types': 29.5.0 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.9 + import-local: 3.0.3 + jest-config: 29.5.0 + jest-util: 29.5.0 + jest-validate: 29.5.0 + prompts: 2.4.2 + yargs: 17.5.1 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: false + + /jest-cli/29.5.0_@types+node@16.18.21: + resolution: {integrity: sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/types': 29.5.0 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.9 + import-local: 3.0.3 + jest-config: 29.5.0_@types+node@16.18.21 + jest-util: 29.5.0 + jest-validate: 29.5.0 + prompts: 2.4.2 + yargs: 17.5.1 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + /jest-config/24.9.0: resolution: {integrity: sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==} engines: {node: '>= 6'} @@ -29392,6 +30782,82 @@ packages: - supports-color - utf-8-validate + /jest-config/29.5.0: + resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.21.3 + '@jest/test-sequencer': 29.5.0 + '@jest/types': 29.5.0 + babel-jest: 29.5.0_@babel+core@7.21.3 + chalk: 4.1.2 + ci-info: 3.2.0 + deepmerge: 4.3.0 + glob: 7.2.3 + graceful-fs: 4.2.9 + jest-circus: 29.5.0 + jest-environment-node: 29.5.0 + jest-get-type: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.5.0 + jest-runner: 29.5.0 + jest-util: 29.5.0 + jest-validate: 29.5.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.5.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: false + + /jest-config/29.5.0_@types+node@16.18.21: + resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.21.3 + '@jest/test-sequencer': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 16.18.21 + babel-jest: 29.5.0_@babel+core@7.21.3 + chalk: 4.1.2 + ci-info: 3.2.0 + deepmerge: 4.3.0 + glob: 7.2.3 + graceful-fs: 4.2.9 + jest-circus: 29.5.0 + jest-environment-node: 29.5.0 + jest-get-type: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.5.0 + jest-runner: 29.5.0 + jest-util: 29.5.0 + jest-validate: 29.5.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.5.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + /jest-dev-server/4.4.0: resolution: {integrity: sha512-STEHJ3iPSC8HbrQ3TME0ozGX2KT28lbT4XopPxUm2WimsX3fcB3YOptRh12YphQisMhfqNSNTZUmWyT3HEXS2A==} dependencies: @@ -29458,6 +30924,15 @@ packages: jest-get-type: 27.5.1 pretty-format: 27.5.1 + /jest-diff/29.5.0: + resolution: {integrity: sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.4.3 + jest-get-type: 29.4.3 + pretty-format: 29.5.0 + /jest-docblock/24.9.0: resolution: {integrity: sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==} engines: {node: '>= 6'} @@ -29485,6 +30960,12 @@ packages: dependencies: detect-newline: 3.1.0 + /jest-docblock/29.4.3: + resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + /jest-each/24.9.0: resolution: {integrity: sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==} engines: {node: '>= 6'} @@ -29530,6 +31011,16 @@ packages: jest-util: 27.5.1 pretty-format: 27.5.1 + /jest-each/29.5.0: + resolution: {integrity: sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + chalk: 4.1.2 + jest-get-type: 29.4.3 + jest-util: 29.5.0 + pretty-format: 29.5.0 + /jest-environment-jsdom/24.9.0: resolution: {integrity: sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==} engines: {node: '>= 6'} @@ -29645,6 +31136,17 @@ packages: jest-mock: 27.5.1 jest-util: 27.5.1 + /jest-environment-node/29.5.0: + resolution: {integrity: sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/fake-timers': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 16.18.21 + jest-mock: 29.5.0 + jest-util: 29.5.0 + /jest-environment-puppeteer/4.4.0: resolution: {integrity: sha512-iV8S8+6qkdTM6OBR/M9gKywEk8GDSOe05hspCs5D8qKSwtmlUfdtHfB4cakdc68lC6YfK3AUsLirpfgodCHjzQ==} dependencies: @@ -29687,6 +31189,10 @@ packages: resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-get-type/29.4.3: + resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /jest-haste-map/24.9.0: resolution: {integrity: sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==} engines: {node: '>= 6'} @@ -29771,6 +31277,24 @@ packages: optionalDependencies: fsevents: 2.3.2 + /jest-haste-map/29.5.0: + resolution: {integrity: sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@types/graceful-fs': 4.1.5 + '@types/node': 16.18.21 + anymatch: 3.1.2 + fb-watchman: 2.0.1 + graceful-fs: 4.2.9 + jest-regex-util: 29.4.3 + jest-util: 29.5.0 + jest-worker: 29.5.0 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.2 + /jest-jasmine2/24.9.0: resolution: {integrity: sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==} engines: {node: '>= 6'} @@ -29908,6 +31432,13 @@ packages: jest-get-type: 27.5.1 pretty-format: 27.5.1 + /jest-leak-detector/29.5.0: + resolution: {integrity: sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.4.3 + pretty-format: 29.5.0 + /jest-matcher-utils/24.9.0: resolution: {integrity: sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==} engines: {node: '>= 6'} @@ -29946,6 +31477,15 @@ packages: jest-get-type: 27.5.1 pretty-format: 27.5.1 + /jest-matcher-utils/29.5.0: + resolution: {integrity: sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.5.0 + jest-get-type: 29.4.3 + pretty-format: 29.5.0 + /jest-message-util/24.9.0: resolution: {integrity: sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==} engines: {node: '>= 6'} @@ -30005,6 +31545,20 @@ packages: slash: 3.0.0 stack-utils: 2.0.5 + /jest-message-util/29.5.0: + resolution: {integrity: sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.18.6 + '@jest/types': 29.5.0 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.9 + micromatch: 4.0.5 + pretty-format: 29.5.0 + slash: 3.0.0 + stack-utils: 2.0.5 + /jest-mock-extended/1.0.18_cnngzrja2umb46xxazlucyx2qu: resolution: {integrity: sha512-qf1n7lIa2dTxxPIBr+FlXrbj3hnV1sG9DPZsrr2H/8W+Jw0wt6OmeOQsPcjRuW8EXIECC9pDXsSIfEdn+HP7JQ==} peerDependencies: @@ -30045,6 +31599,14 @@ packages: '@jest/types': 27.5.1 '@types/node': 16.18.21 + /jest-mock/29.5.0: + resolution: {integrity: sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@types/node': 16.18.21 + jest-util: 29.5.0 + /jest-pnp-resolver/1.2.2_jest-resolve@24.9.0: resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} engines: {node: '>=6'} @@ -30092,6 +31654,17 @@ packages: dependencies: jest-resolve: 27.5.1 + /jest-pnp-resolver/1.2.2_jest-resolve@29.5.0: + resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.5.0 + /jest-puppeteer/4.4.0_puppeteer-core@3.0.0: resolution: {integrity: sha512-ZaiCTlPZ07B9HW0erAWNX6cyzBqbXMM7d2ugai4epBDKpKvRDpItlRQC6XjERoJELKZsPziFGS0OhhUvTvQAXA==} peerDependencies: @@ -30135,6 +31708,10 @@ packages: resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + /jest-regex-util/29.4.3: + resolution: {integrity: sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /jest-resolve-dependencies/24.9.0: resolution: {integrity: sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==} engines: {node: '>= 6'} @@ -30176,6 +31753,15 @@ packages: transitivePeerDependencies: - supports-color + /jest-resolve-dependencies/29.5.0: + resolution: {integrity: sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.4.3 + jest-snapshot: 29.5.0 + transitivePeerDependencies: + - supports-color + /jest-resolve/24.9.0: resolution: {integrity: sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==} engines: {node: '>= 6'} @@ -30231,15 +31817,29 @@ packages: resolve.exports: 1.1.0 slash: 3.0.0 - /jest-runner-groups/2.2.0_izd2bb4ook7j3zolwvt7oz6lf4: + /jest-resolve/29.5.0: + resolution: {integrity: sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.9 + jest-haste-map: 29.5.0 + jest-pnp-resolver: 1.2.2_jest-resolve@29.5.0 + jest-util: 29.5.0 + jest-validate: 29.5.0 + resolve: 1.22.1 + resolve.exports: 2.0.2 + slash: 3.0.0 + + /jest-runner-groups/2.2.0_si6aka6xkbrcvhz7vk2qr7go64: resolution: {integrity: sha512-Sp/B9ZX0CDAKa9dIkgH0sGyl2eDuScV4SVvOxqhBMxqWpsNAkmol/C58aTFmPWZj+C0ZTW1r1BSu66MTCN+voA==} engines: {node: '>= 10.14.2'} peerDependencies: jest-docblock: '>= 24' jest-runner: '>= 24' dependencies: - jest-docblock: 27.5.1 - jest-runner: 27.5.1 + jest-docblock: 29.4.3 + jest-runner: 29.5.0 dev: false /jest-runner/24.9.0: @@ -30364,6 +31964,34 @@ packages: - supports-color - utf-8-validate + /jest-runner/29.5.0: + resolution: {integrity: sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.5.0 + '@jest/environment': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 16.18.21 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.9 + jest-docblock: 29.4.3 + jest-environment-node: 29.5.0 + jest-haste-map: 29.5.0 + jest-leak-detector: 29.5.0 + jest-message-util: 29.5.0 + jest-resolve: 29.5.0 + jest-runtime: 29.5.0 + jest-util: 29.5.0 + jest-watcher: 29.5.0 + jest-worker: 29.5.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + /jest-runtime/24.9.0: resolution: {integrity: sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==} engines: {node: '>= 6'} @@ -30505,6 +32133,35 @@ packages: transitivePeerDependencies: - supports-color + /jest-runtime/29.5.0: + resolution: {integrity: sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/fake-timers': 29.5.0 + '@jest/globals': 29.5.0 + '@jest/source-map': 29.4.3 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 16.18.21 + chalk: 4.1.2 + cjs-module-lexer: 1.2.2 + collect-v8-coverage: 1.0.1 + glob: 7.2.3 + graceful-fs: 4.2.9 + jest-haste-map: 29.5.0 + jest-message-util: 29.5.0 + jest-mock: 29.5.0 + jest-regex-util: 29.4.3 + jest-resolve: 29.5.0 + jest-snapshot: 29.5.0 + jest-util: 29.5.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + /jest-serializer/24.9.0: resolution: {integrity: sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==} engines: {node: '>= 6'} @@ -30626,6 +32283,36 @@ packages: transitivePeerDependencies: - supports-color + /jest-snapshot/29.5.0: + resolution: {integrity: sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.21.3 + '@babel/generator': 7.21.3 + '@babel/plugin-syntax-jsx': 7.18.6_@babel+core@7.21.3 + '@babel/plugin-syntax-typescript': 7.18.6_@babel+core@7.21.3 + '@babel/traverse': 7.21.3 + '@babel/types': 7.21.3 + '@jest/expect-utils': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/babel__traverse': 7.14.2 + '@types/prettier': 2.4.2 + babel-preset-current-node-syntax: 1.0.1_@babel+core@7.21.3 + chalk: 4.1.2 + expect: 29.5.0 + graceful-fs: 4.2.9 + jest-diff: 29.5.0 + jest-get-type: 29.4.3 + jest-matcher-utils: 29.5.0 + jest-message-util: 29.5.0 + jest-util: 29.5.0 + natural-compare: 1.4.0 + pretty-format: 29.5.0 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + /jest-util/24.9.0: resolution: {integrity: sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==} engines: {node: '>= 6'} @@ -30679,6 +32366,17 @@ packages: graceful-fs: 4.2.9 picomatch: 2.3.0 + /jest-util/29.5.0: + resolution: {integrity: sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@types/node': 16.18.21 + chalk: 4.1.2 + ci-info: 3.2.0 + graceful-fs: 4.2.9 + picomatch: 2.3.1 + /jest-validate/24.9.0: resolution: {integrity: sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==} engines: {node: '>= 6'} @@ -30725,6 +32423,17 @@ packages: leven: 3.1.0 pretty-format: 27.5.1 + /jest-validate/29.5.0: + resolution: {integrity: sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + camelcase: 6.2.1 + chalk: 4.1.2 + jest-get-type: 29.4.3 + leven: 3.1.0 + pretty-format: 29.5.0 + /jest-watcher/24.9.0: resolution: {integrity: sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==} engines: {node: '>= 6'} @@ -30777,6 +32486,19 @@ packages: jest-util: 27.5.1 string-length: 4.0.2 + /jest-watcher/29.5.0: + resolution: {integrity: sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 16.18.21 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.5.0 + string-length: 4.0.2 + /jest-worker/24.9.0: resolution: {integrity: sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==} engines: {node: '>= 6'} @@ -30809,6 +32531,15 @@ packages: merge-stream: 2.0.0 supports-color: 8.1.1 + /jest-worker/29.5.0: + resolution: {integrity: sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@types/node': 16.18.21 + jest-util: 29.5.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + /jest/24.9.0: resolution: {integrity: sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==} engines: {node: '>= 6'} @@ -30873,6 +32604,46 @@ packages: - ts-node - utf-8-validate + /jest/29.5.0: + resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.5.0 + '@jest/types': 29.5.0 + import-local: 3.0.3 + jest-cli: 29.5.0 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: false + + /jest/29.5.0_@types+node@16.18.21: + resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.5.0 + '@jest/types': 29.5.0 + import-local: 3.0.3 + jest-cli: 29.5.0_@types+node@16.18.21 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + /jmespath/0.16.0: resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==} engines: {node: '>= 0.6.0'} @@ -33919,7 +35690,7 @@ packages: normalize-package-data: 3.0.3 qqjs: 0.3.11 semver: 7.3.7 - tslib: 2.3.1 + tslib: 2.5.0 yeoman-environment: 3.10.0_7yoz4vugw4qcykie6sit5r22dm yeoman-generator: 5.7.0_yeoman-environment@3.10.0 yosay: 2.0.2 @@ -35734,6 +37505,14 @@ packages: ansi-styles: 5.2.0 react-is: 17.0.2 + /pretty-format/29.5.0: + resolution: {integrity: sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.4.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + /pretty-hrtime/1.0.3: resolution: {integrity: sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=} engines: {node: '>= 0.8'} @@ -36053,6 +37832,9 @@ packages: - supports-color - utf-8-validate + /pure-rand/6.0.1: + resolution: {integrity: sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==} + /q/1.5.1: resolution: {integrity: sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=} engines: {node: '>=0.6.0', teleport: '>=0.2.0'} @@ -36572,6 +38354,9 @@ packages: /react-is/17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + /react-is/18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + /react-lifecycles-compat/3.0.4: resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} @@ -36880,7 +38665,7 @@ packages: dependencies: object-assign: 4.1.1 react: 18.1.0 - react-is: 17.0.2 + react-is: 18.2.0 /react-sizeme/3.0.2: resolution: {integrity: sha512-xOIAOqqSSmKlKFJLO3inBQBdymzDuXx4iuwkNcJmC96jeiOg5ojByvL+g3MW9LPEsojLbC6pf68zOfobK8IPlw==} @@ -37792,6 +39577,10 @@ packages: resolution: {integrity: sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==} engines: {node: '>=10'} + /resolve.exports/2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + /resolve/1.1.7: resolution: {integrity: sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==} @@ -38771,6 +40560,12 @@ packages: atob: 2.1.2 decode-uri-component: 0.2.0 + /source-map-support/0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + /source-map-support/0.5.20: resolution: {integrity: sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==} dependencies: @@ -40649,6 +42444,40 @@ packages: yargs-parser: 20.2.9 dev: true + /ts-jest/29.1.0_q744lk64cx4tbpg7zn4yr5nesi: + resolution: {integrity: sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.21.3 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.5.0_@types+node@16.18.21 + jest-util: 29.5.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.3.8 + typescript: 4.9.5 + yargs-parser: 21.1.1 + dev: true + /ts-loader/9.4.1_t37drsge5fnqkss6ynqsf64hyi: resolution: {integrity: sha512-384TYAqGs70rn9F0VBnh6BPTfhga7yFNdC5gXbQpDrBj9/KsT4iRkGqKXhziofHOlE2j6YEaiTYVGKKvPhGWvw==} engines: {node: '>=12.0.0'} @@ -40722,6 +42551,7 @@ packages: /tslib/2.3.1: resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==} + dev: true /tslib/2.5.0: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} @@ -41598,6 +43428,14 @@ packages: convert-source-map: 1.8.0 source-map: 0.7.3 + /v8-to-istanbul/9.1.0: + resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.17 + '@types/istanbul-lib-coverage': 2.0.3 + convert-source-map: 1.8.0 + /v8flags/3.1.3: resolution: {integrity: sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==} engines: {node: '>= 0.10'} @@ -42828,6 +44666,13 @@ packages: signal-exit: 3.0.7 dev: true + /write-file-atomic/4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + /write-json-file/4.3.0: resolution: {integrity: sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ==} engines: {node: '>=8.3'} @@ -43040,7 +44885,6 @@ packages: /yargs-parser/21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - dev: true /yargs-unparser/1.6.0: resolution: {integrity: sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==} @@ -43119,7 +44963,6 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true /yargs/4.8.1: resolution: {integrity: sha1-wMQpJMpKqmsObaFznfshZDn53cA=} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 39e5ac30da4..040f0c3699f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -11,3 +11,4 @@ packages: - 'tools/cli-core' - 'tools/version-bump' - 'tools/storybook' + - 'tools/monorepo-utils' diff --git a/tools/code-analyzer/.eslintrc.js b/tools/code-analyzer/.eslintrc.js index b1b0859966c..e4d185d8cd1 100644 --- a/tools/code-analyzer/.eslintrc.js +++ b/tools/code-analyzer/.eslintrc.js @@ -1,4 +1,4 @@ module.exports = { - extends: ['plugin:@woocommerce/eslint-plugin/recommended'], + extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ], root: true, }; diff --git a/tools/monorepo-utils/.eslintignore b/tools/monorepo-utils/.eslintignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/monorepo-utils/.eslintrc.js b/tools/monorepo-utils/.eslintrc.js new file mode 100644 index 00000000000..e4d185d8cd1 --- /dev/null +++ b/tools/monorepo-utils/.eslintrc.js @@ -0,0 +1,4 @@ +module.exports = { + extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ], + root: true, +}; diff --git a/tools/monorepo-utils/.gitignore b/tools/monorepo-utils/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/monorepo-utils/README.md b/tools/monorepo-utils/README.md new file mode 100644 index 00000000000..bb3bfa3b665 --- /dev/null +++ b/tools/monorepo-utils/README.md @@ -0,0 +1,5 @@ +# Monorepo Utils + +## Description + +Monorepo utilities and tooling. diff --git a/tools/monorepo-utils/jest.config.js b/tools/monorepo-utils/jest.config.js new file mode 100644 index 00000000000..da5d4c982ec --- /dev/null +++ b/tools/monorepo-utils/jest.config.js @@ -0,0 +1,6 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testPathIgnorePatterns: [ '/dist/', '/node_modules/' ], +}; diff --git a/tools/monorepo-utils/package.json b/tools/monorepo-utils/package.json new file mode 100644 index 00000000000..64116d6a361 --- /dev/null +++ b/tools/monorepo-utils/package.json @@ -0,0 +1,35 @@ +{ + "name": "@woocommerce/monorepo-utils", + "version": "0.0.1", + "description": "WooCommerce Monorepo utility tooling.", + "author": "Automattic", + "homepage": "https://github.com/woocommerce/woocommerce", + "license": "GPLv2", + "repository": "woocommerce/woocommerce", + "dependencies": { + "chalk": "^4.1.2", + "commander": "^9.4.0", + "@commander-js/extra-typings": "^0.1.0", + "dotenv": "^10.0.0" + }, + "devDependencies": { + "@types/jest": "^27.4.1", + "@types/node": "^16.18.18", + "@woocommerce/eslint-plugin": "workspace:*", + "eslint": "^8.32.0", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "typescript": "^4.9.5" + }, + "scripts": { + "build": "tsc", + "start": "tsc --watch", + "lint": "eslint . --ext .ts", + "test": "jest" + }, + "engines": { + "node": "^16.13.1", + "pnpm": "^7.13.3" + }, + "types": "dist/index.d.ts" +} diff --git a/tools/monorepo-utils/src/code-freeze/README.md b/tools/monorepo-utils/src/code-freeze/README.md new file mode 100644 index 00000000000..89b8d9c0e03 --- /dev/null +++ b/tools/monorepo-utils/src/code-freeze/README.md @@ -0,0 +1,3 @@ +# Code Freeze CLI Utility + +CLI for performing Monorepo utilities relating to Code Freeze diff --git a/tools/monorepo-utils/src/code-freeze/commands/index.ts b/tools/monorepo-utils/src/code-freeze/commands/index.ts new file mode 100644 index 00000000000..ae036598b46 --- /dev/null +++ b/tools/monorepo-utils/src/code-freeze/commands/index.ts @@ -0,0 +1,15 @@ +/** + * External dependencies + */ +import { Command } from '@commander-js/extra-typings'; + +/** + * Internal dependencies + */ +import { verifyDayCommand } from './verify-day'; + +const program = new Command( 'code-freeze' ) + .description( 'Code freeze utilities' ) + .addCommand( verifyDayCommand ); + +export default program; diff --git a/tools/monorepo-utils/src/code-freeze/commands/verify-day.ts b/tools/monorepo-utils/src/code-freeze/commands/verify-day.ts new file mode 100644 index 00000000000..c307ab06814 --- /dev/null +++ b/tools/monorepo-utils/src/code-freeze/commands/verify-day.ts @@ -0,0 +1,21 @@ +/** + * External dependencies + */ +import { Command } from '@commander-js/extra-typings'; + +/** + * Internal dependencies + */ +import { verifyDay } from '../utils/index'; + +export const verifyDayCommand = new Command( 'verify-day' ) + .description( 'Verify if today is the code freeze day' ) + .option( + '-o, --override ', + "Time Override: The time to use in checking whether the action should run (default: 'now')." + ) + .action( () => { + console.log( verifyDay() ); + + process.exit( 0 ); + } ); diff --git a/tools/monorepo-utils/src/code-freeze/utils/__tests__/index.ts b/tools/monorepo-utils/src/code-freeze/utils/__tests__/index.ts new file mode 100644 index 00000000000..f4ac07061de --- /dev/null +++ b/tools/monorepo-utils/src/code-freeze/utils/__tests__/index.ts @@ -0,0 +1,10 @@ +/** + * Internal dependencies + */ +import { verifyDay } from '../index'; + +describe( 'verifyDay', () => { + it( 'should return a string', () => { + expect( verifyDay() ).toBe( 'Today is a good day to code freeze!' ); + } ); +} ); diff --git a/tools/monorepo-utils/src/code-freeze/utils/index.ts b/tools/monorepo-utils/src/code-freeze/utils/index.ts new file mode 100644 index 00000000000..b06bc3469a2 --- /dev/null +++ b/tools/monorepo-utils/src/code-freeze/utils/index.ts @@ -0,0 +1,3 @@ +export const verifyDay = () => { + return 'Today is a good day to code freeze!'; +}; diff --git a/tools/monorepo-utils/src/index.ts b/tools/monorepo-utils/src/index.ts new file mode 100755 index 00000000000..2fb352fc639 --- /dev/null +++ b/tools/monorepo-utils/src/index.ts @@ -0,0 +1,17 @@ +#! /usr/bin/env node +/** + * External dependencies + */ +import { Command } from '@commander-js/extra-typings'; + +/** + * Internal dependencies + */ +import CodeFreeze from './code-freeze/commands'; + +const program = new Command() + .name( 'utils' ) + .description( 'Monorepo utilities' ) + .addCommand( CodeFreeze ); + +program.parse( process.argv ); diff --git a/tools/monorepo-utils/tsconfig.json b/tools/monorepo-utils/tsconfig.json new file mode 100644 index 00000000000..71ea5a3fd96 --- /dev/null +++ b/tools/monorepo-utils/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "target": "es6", + "module": "commonjs", + "esModuleInterop": true, + "moduleResolution": "node", + "typeRoots": [ + "./typings", + "./node_modules/@types", + "./node_modules/@commander-js" + ] + } +} From 7fe96ac988e44f2b9ec5481199f909d0cf6f696b Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Tue, 11 Apr 2023 14:36:32 -0700 Subject: [PATCH 177/466] Don't hardcode table prefixes --- .../tests/php/src/Utilities/OrderUtilTest.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/plugins/woocommerce/tests/php/src/Utilities/OrderUtilTest.php b/plugins/woocommerce/tests/php/src/Utilities/OrderUtilTest.php index a1726b28992..02a004be351 100644 --- a/plugins/woocommerce/tests/php/src/Utilities/OrderUtilTest.php +++ b/plugins/woocommerce/tests/php/src/Utilities/OrderUtilTest.php @@ -38,39 +38,47 @@ class OrderUtilTest extends \WC_Unit_Test_Case { * @testdox `get_table_for_orders` should return the name of the posts table when HPOS is not in use. */ public function test_get_table_for_orders_posts() { + global $wpdb; + OrderHelper::toggle_cot( false ); $table_name = OrderUtil::get_table_for_orders(); - $this->assertEquals( 'wptests_posts', $table_name ); + $this->assertEquals( $wpdb->posts, $table_name ); } /** * @testdox `get_table_for_orders` should return the name of the orders table when HPOS is in use. */ public function test_get_table_for_orders_hpos() { + global $wpdb; + OrderHelper::toggle_cot( true ); $table_name = OrderUtil::get_table_for_orders(); - $this->assertEquals( 'wptests_wc_orders', $table_name ); + $this->assertEquals( "{$wpdb->prefix}wc_orders", $table_name ); } /** * @testdox `get_table_for_order_meta` should return the name of the postmeta table when HPOS is not in use. */ public function test_get_table_for_order_meta_posts() { + global $wpdb; + OrderHelper::toggle_cot( false ); $table_name = OrderUtil::get_table_for_order_meta(); - $this->assertEquals( 'wptests_postmeta', $table_name ); + $this->assertEquals( $wpdb->postmeta, $table_name ); } /** * @testdox `get_table_for_order_meta` should return the name of the orders meta table when HPOS is in use. */ public function test_get_table_for_order_meta_hpos() { + global $wpdb; + OrderHelper::toggle_cot( true ); $table_name = OrderUtil::get_table_for_order_meta(); - $this->assertEquals( 'wptests_wc_orders_meta', $table_name ); + $this->assertEquals( "{$wpdb->prefix}wc_orders_meta", $table_name ); } } From d4d375e8742f3a95aa565b3d8c50f7bed16271c0 Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Tue, 11 Apr 2023 15:51:38 -0700 Subject: [PATCH 178/466] WC_Data: Add method `delete_matched_meta_data` Brings the CRUD layer's meta data handling closer to parity with WP by allowing for selectively deleting meta entries with a specific key only if they contain a specific value. Fixes #37650 --- .../includes/abstracts/abstract-wc-data.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/plugins/woocommerce/includes/abstracts/abstract-wc-data.php b/plugins/woocommerce/includes/abstracts/abstract-wc-data.php index 863db390320..2f19cb6d1f0 100644 --- a/plugins/woocommerce/includes/abstracts/abstract-wc-data.php +++ b/plugins/woocommerce/includes/abstracts/abstract-wc-data.php @@ -506,6 +506,26 @@ abstract class WC_Data { } } + /** + * Delete meta data. + * + * @since 7.7.0 + * @param string $key Meta key. + * @param mixed $value Meta value. Entries will only be removed that match the value. + */ + public function delete_matched_meta_data( $key, $value ) { + $this->maybe_read_meta_data(); + $array_keys = array_keys( wp_list_pluck( $this->meta_data, 'key' ), $key, true ); + + if ( $array_keys ) { + foreach ( $array_keys as $array_key ) { + if ( $value === $this->meta_data[ $array_key ]->value ) { + $this->meta_data[ $array_key ]->value = null; + } + } + } + } + /** * Delete meta data. * From d8ec0490cb534505d1c4003af76648756cc72e94 Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Tue, 11 Apr 2023 15:55:27 -0700 Subject: [PATCH 179/466] Add unit test --- .../tests/legacy/unit-tests/crud/data.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/plugins/woocommerce/tests/legacy/unit-tests/crud/data.php b/plugins/woocommerce/tests/legacy/unit-tests/crud/data.php index e4a4c4ce33b..dd1c2c6bcc2 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/crud/data.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/crud/data.php @@ -325,6 +325,30 @@ class WC_Tests_CRUD_Data extends WC_Unit_Test_Case { $this->assertEmpty( $object->get_meta( 'test_meta_key' ) ); } + /** + * Test deleting meta selectively. + */ + public function test_delete_matched_meta_data() { + $object = $this->create_test_post(); + $object_id = $object->get_id(); + add_metadata( 'post', $object_id, 'test_meta_key', 'val1' ); + add_metadata( 'post', $object_id, 'test_meta_key', 'val2' ); + add_metadata( 'post', $object_id, 'test_meta_key', array( 'foo', 'bar' ) ); + $object = new WC_Mock_WC_Data( $object_id ); + + $this->assertCount( 3, $object->get_meta( 'test_meta_key', false ) ); + + $object->delete_matched_meta_data( 'test_meta_key', 'val1' ); + $this->assertCount( 2, $object->get_meta( 'test_meta_key', false ) ); + + $object->delete_matched_meta_data( 'test_meta_key', array( 'bar', 'baz' ) ); + $this->assertCount( 2, $object->get_meta( 'test_meta_key', false ) ); + + $object->delete_matched_meta_data( 'test_meta_key', array( 'foo', 'bar' ) ); + $this->assertCount( 1, $object->get_meta( 'test_meta_key', false ) ); + + $this->assertEquals( 'val2', $object->get_meta( 'test_meta_key' ) ); + } /** * Test saving metadata (Actually making sure changes are written to DB). From b389a4e8aef1e3e0ee7863470b70be1746abc5b1 Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Tue, 11 Apr 2023 15:58:27 -0700 Subject: [PATCH 180/466] Add changelog file --- plugins/woocommerce/changelog/fix-37650-delete-meta-data | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/fix-37650-delete-meta-data diff --git a/plugins/woocommerce/changelog/fix-37650-delete-meta-data b/plugins/woocommerce/changelog/fix-37650-delete-meta-data new file mode 100644 index 00000000000..a9c72a79091 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-37650-delete-meta-data @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add method delete_matched_meta_data to WC_Data objects From 3c64b953a0b3d3b32deaa663074092b28026ecc9 Mon Sep 17 00:00:00 2001 From: Corey McKrill <916023+coreymckrill@users.noreply.github.com> Date: Tue, 11 Apr 2023 16:02:01 -0700 Subject: [PATCH 181/466] Update doc block --- plugins/woocommerce/includes/abstracts/abstract-wc-data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/woocommerce/includes/abstracts/abstract-wc-data.php b/plugins/woocommerce/includes/abstracts/abstract-wc-data.php index 2f19cb6d1f0..06ac6cc11ae 100644 --- a/plugins/woocommerce/includes/abstracts/abstract-wc-data.php +++ b/plugins/woocommerce/includes/abstracts/abstract-wc-data.php @@ -507,7 +507,7 @@ abstract class WC_Data { } /** - * Delete meta data. + * Delete meta data with a matching value. * * @since 7.7.0 * @param string $key Meta key. From a7401265644d72ed30c4abef14bb1449eb252761 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Apr 2023 08:59:53 +0200 Subject: [PATCH 182/466] Delete changelog files based on PR 37641 (#37653) Delete changelog files for 37641 Co-authored-by: WooCommerce Bot --- plugins/woocommerce/changelog/fix-gutengerg15-5-update-issues | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 plugins/woocommerce/changelog/fix-gutengerg15-5-update-issues diff --git a/plugins/woocommerce/changelog/fix-gutengerg15-5-update-issues b/plugins/woocommerce/changelog/fix-gutengerg15-5-update-issues deleted file mode 100644 index 5e9a1834400..00000000000 --- a/plugins/woocommerce/changelog/fix-gutengerg15-5-update-issues +++ /dev/null @@ -1,4 +0,0 @@ -Significance: patch -Type: fix - -Fix incorrect usage of dispatch, useSelect, and setState calls in homescreen along with settings and onboarding package From dd138a394f3a84ae4204fbb6f1dd48b8f4709ee3 Mon Sep 17 00:00:00 2001 From: rodelgc Date: Wed, 12 Apr 2023 15:35:27 +0800 Subject: [PATCH 183/466] Remove unnecessary concatenation --- plugins/woocommerce/tests/e2e-pw/utils/wordpress.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/woocommerce/tests/e2e-pw/utils/wordpress.js b/plugins/woocommerce/tests/e2e-pw/utils/wordpress.js index 7904302dbed..018382e7e19 100644 --- a/plugins/woocommerce/tests/e2e-pw/utils/wordpress.js +++ b/plugins/woocommerce/tests/e2e-pw/utils/wordpress.js @@ -25,12 +25,6 @@ const getPreviousTwoVersions = async () => { continue; } - const hasNoPatchNumber = thisVersion.split( '.' ).length === 2; - - if ( hasNoPatchNumber ) { - thisVersion = thisVersion.concat( '.0' ); - } - prevTwo.push( thisVersion ); if ( prevTwo.length === 2 ) { From 60a02f2edf76844d42393e131f187bd2f02111df Mon Sep 17 00:00:00 2001 From: rodelgc Date: Wed, 12 Apr 2023 15:50:48 +0800 Subject: [PATCH 184/466] Fix WP L-2 version retrieval --- .../tests/e2e-pw/utils/wordpress.js | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/plugins/woocommerce/tests/e2e-pw/utils/wordpress.js b/plugins/woocommerce/tests/e2e-pw/utils/wordpress.js index 018382e7e19..ef193635474 100644 --- a/plugins/woocommerce/tests/e2e-pw/utils/wordpress.js +++ b/plugins/woocommerce/tests/e2e-pw/utils/wordpress.js @@ -14,33 +14,35 @@ const getPreviousTwoVersions = async () => { .filter( ( version ) => body[ version ] === 'outdated' ) .sort() .reverse(); - const latestMinorVersion = allVersions + const latestMajorAndMinorNumbers = allVersions .find( ( version ) => body[ version ] === 'latest' ) .match( /^\d+.\d+/ )[ 0 ]; - const prevTwo = []; + const latestMinus1 = previousStableVersions.find( + ( version ) => ! version.startsWith( latestMajorAndMinorNumbers ) + ); - for ( let thisVersion of previousStableVersions ) { - if ( thisVersion.startsWith( latestMinorVersion ) ) { - continue; - } + const latestMinus1MajorAndMinorNumbers = latestMinus1.match( + /^\d+.\d+/ + )[ 0 ]; - prevTwo.push( thisVersion ); - - if ( prevTwo.length === 2 ) { - break; - } - } + const latestMinus2 = previousStableVersions.find( + ( version ) => + ! ( + version.startsWith( latestMajorAndMinorNumbers ) || + version.startsWith( latestMinus1MajorAndMinorNumbers ) + ) + ); const matrix = { version: [ { - number: prevTwo[ 0 ], + number: latestMinus1, description: 'WP Latest-1', env_description: 'wp-latest-1', }, { - number: prevTwo[ 1 ], + number: latestMinus2, description: 'WP Latest-2', env_description: 'wp-latest-2', }, From 61da4c6161dfc5ba6a1dfbb78ce22934aab27ec3 Mon Sep 17 00:00:00 2001 From: Chris Greys Date: Wed, 12 Apr 2023 16:52:18 +0900 Subject: [PATCH 185/466] Dev - Allow to filter wc_help_tip output (#37485) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Néstor Soriano --- plugins/woocommerce/changelog/fix-36543 | 4 ++++ .../woocommerce/includes/wc-core-functions.php | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 plugins/woocommerce/changelog/fix-36543 diff --git a/plugins/woocommerce/changelog/fix-36543 b/plugins/woocommerce/changelog/fix-36543 new file mode 100644 index 00000000000..483efbe86c4 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-36543 @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Dev - Allow to filter wc_help_tip diff --git a/plugins/woocommerce/includes/wc-core-functions.php b/plugins/woocommerce/includes/wc-core-functions.php index 68d88a6ec12..9c1d0a57248 100644 --- a/plugins/woocommerce/includes/wc-core-functions.php +++ b/plugins/woocommerce/includes/wc-core-functions.php @@ -1593,12 +1593,24 @@ function wc_back_link( $label, $url ) { */ function wc_help_tip( $tip, $allow_html = false ) { if ( $allow_html ) { - $tip = wc_sanitize_tooltip( $tip ); + $sanitized_tip = wc_sanitize_tooltip( $tip ); } else { - $tip = esc_attr( $tip ); + $sanitized_tip = esc_attr( $tip ); } - return ''; + /** + * Filter the help tip. + * + * @since 7.7.0 + * + * @param string $tip_html Help tip HTML. + * @param string $sanitized_tip Sanitized help tip text. + * @param string $tip Original help tip text. + * @param bool $allow_html Allow sanitized HTML if true or escape. + * + * @return string + */ + return apply_filters( 'wc_help_tip', '', $sanitized_tip, $tip, $allow_html ); } /** From e8c8581a4ec4b2bde461835e702e14ebbffb6965 Mon Sep 17 00:00:00 2001 From: Chris Lilitsas Date: Wed, 12 Apr 2023 10:54:58 +0300 Subject: [PATCH 186/466] Fix TT2 styles and typography in the single product's attributes table (#37639) --- .../changelog/fix-37636-attributes-table | 4 ++++ .../client/legacy/css/twenty-twenty-two.scss | 22 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 plugins/woocommerce/changelog/fix-37636-attributes-table diff --git a/plugins/woocommerce/changelog/fix-37636-attributes-table b/plugins/woocommerce/changelog/fix-37636-attributes-table new file mode 100644 index 00000000000..547c8a21564 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-37636-attributes-table @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fixed the attributes table styling in TT2 tabs content area diff --git a/plugins/woocommerce/client/legacy/css/twenty-twenty-two.scss b/plugins/woocommerce/client/legacy/css/twenty-twenty-two.scss index be8519c50e1..dfd10c6d8f7 100644 --- a/plugins/woocommerce/client/legacy/css/twenty-twenty-two.scss +++ b/plugins/woocommerce/client/legacy/css/twenty-twenty-two.scss @@ -594,9 +594,29 @@ ul.wc-tabs { font-size: var(--wp--preset--font-size--small); margin-left: 1em; - h2 { + // Hide repeated heading. + h2:first-of-type { display: none; } + + // Attributes table styles. + table.woocommerce-product-attributes { + tbody { + + td, th { + padding: 0.2rem 0.2rem 0.2rem 0; + + p { + margin: 0; + } + } + + th { + text-align: left; + padding-right: 1rem; + } + } + } } /** From b4540f61d5e4cccb75090230b712abf28a1dfd1b Mon Sep 17 00:00:00 2001 From: rodelgc Date: Wed, 12 Apr 2023 15:56:20 +0800 Subject: [PATCH 187/466] Add changelog --- plugins/woocommerce/changelog/e2e-fix-wp-l-2-retrieval | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/e2e-fix-wp-l-2-retrieval diff --git a/plugins/woocommerce/changelog/e2e-fix-wp-l-2-retrieval b/plugins/woocommerce/changelog/e2e-fix-wp-l-2-retrieval new file mode 100644 index 00000000000..0eb4eaba23a --- /dev/null +++ b/plugins/woocommerce/changelog/e2e-fix-wp-l-2-retrieval @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Fix WP latest-2 version retrieval in the "Smoke test release" workflow. From 77bdd24aa8a3c8314bd65e7cab123d456bc91607 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Wed, 12 Apr 2023 16:46:21 +0800 Subject: [PATCH 188/466] Revert "Check min. WP and PHP versions before suggesting plugins" (#37674) Revert "Check min. WP and PHP versions before suggesting plugins (#37611)" This reverts commit 30536f636ed8b5b2acb57d40ab37c3af8d900084. --- ...ate-16525-check-php-version-for-extensions | 4 --- .../DefaultFreeExtensions.php | 26 ++++++++----------- .../EvaluateExtension.php | 9 ------- 3 files changed, 11 insertions(+), 28 deletions(-) delete mode 100644 plugins/woocommerce/changelog/update-16525-check-php-version-for-extensions diff --git a/plugins/woocommerce/changelog/update-16525-check-php-version-for-extensions b/plugins/woocommerce/changelog/update-16525-check-php-version-for-extensions deleted file mode 100644 index 9cb44346db3..00000000000 --- a/plugins/woocommerce/changelog/update-16525-check-php-version-for-extensions +++ /dev/null @@ -1,4 +0,0 @@ -Significance: minor -Type: update - -Support min_php_version and min_wp_version for the free extensions feed diff --git a/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensions.php b/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensions.php index e893afd283e..99055062238 100644 --- a/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensions.php +++ b/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/DefaultFreeExtensions.php @@ -79,18 +79,17 @@ class DefaultFreeExtensions { public static function get_plugin( $slug ) { $plugins = array( 'google-listings-and-ads' => [ - 'min_php_version' => '7.4', - 'name' => __( 'Google Listings & Ads', 'woocommerce' ), - 'description' => sprintf( + 'name' => __( 'Google Listings & Ads', 'woocommerce' ), + 'description' => sprintf( /* translators: 1: opening product link tag. 2: closing link tag */ __( 'Drive sales with %1$sGoogle Listings and Ads%2$s', 'woocommerce' ), '', '' ), - 'image_url' => plugins_url( '/assets/images/onboarding/google.svg', WC_PLUGIN_FILE ), - 'manage_url' => 'admin.php?page=wc-admin&path=%2Fgoogle%2Fstart', - 'is_built_by_wc' => true, - 'is_visible' => [ + 'image_url' => plugins_url( '/assets/images/onboarding/google.svg', WC_PLUGIN_FILE ), + 'manage_url' => 'admin.php?page=wc-admin&path=%2Fgoogle%2Fstart', + 'is_built_by_wc' => true, + 'is_visible' => [ [ 'type' => 'not', 'operand' => [ @@ -126,12 +125,11 @@ class DefaultFreeExtensions { 'is_built_by_wc' => false, ], 'pinterest-for-woocommerce' => [ - 'name' => __( 'Pinterest for WooCommerce', 'woocommerce' ), - 'description' => __( 'Get your products in front of Pinners searching for ideas and things to buy.', 'woocommerce' ), - 'image_url' => plugins_url( '/assets/images/onboarding/pinterest.png', WC_PLUGIN_FILE ), - 'manage_url' => 'admin.php?page=wc-admin&path=%2Fpinterest%2Flanding', - 'is_built_by_wc' => true, - 'min_php_version' => '7.3', + 'name' => __( 'Pinterest for WooCommerce', 'woocommerce' ), + 'description' => __( 'Get your products in front of Pinners searching for ideas and things to buy.', 'woocommerce' ), + 'image_url' => plugins_url( '/assets/images/onboarding/pinterest.png', WC_PLUGIN_FILE ), + 'manage_url' => 'admin.php?page=wc-admin&path=%2Fpinterest%2Flanding', + 'is_built_by_wc' => true, ], 'pinterest-for-woocommerce:alt' => [ 'name' => __( 'Pinterest for WooCommerce', 'woocommerce' ), @@ -351,7 +349,6 @@ class DefaultFreeExtensions { DefaultPaymentGateways::get_rules_for_cbd( false ), ], 'is_built_by_wc' => true, - 'min_wp_version' => '5.9', ], 'woocommerce-services:shipping' => [ 'description' => sprintf( @@ -519,7 +516,6 @@ class DefaultFreeExtensions { ], ], 'is_built_by_wc' => false, - 'min_wp_version' => '6.0', ], 'mailpoet' => [ 'name' => __( 'MailPoet', 'woocommerce' ), diff --git a/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/EvaluateExtension.php b/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/EvaluateExtension.php index 8256be19ce3..100dd94a125 100644 --- a/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/EvaluateExtension.php +++ b/plugins/woocommerce/src/Internal/Admin/RemoteFreeExtensions/EvaluateExtension.php @@ -21,7 +21,6 @@ class EvaluateExtension { * @return object The evaluated extension. */ public static function evaluate( $extension ) { - global $wp_version; $rule_evaluator = new RuleEvaluator(); if ( isset( $extension->is_visible ) ) { @@ -31,14 +30,6 @@ class EvaluateExtension { $extension->is_visible = true; } - if ( isset( $extension->min_php_version ) ) { - $extension->is_visible = version_compare( PHP_VERSION, $extension->min_php_version, '>=' ); - } - - if ( isset( $extension->min_wp_version ) ) { - $extension->is_visible = version_compare( $wp_version, $extension->min_wp_version, '>=' ); - } - $installed_plugins = PluginsHelper::get_installed_plugin_slugs(); $activated_plugins = PluginsHelper::get_active_plugin_slugs(); $extension->is_installed = in_array( explode( ':', $extension->key )[0], $installed_plugins, true ); From 55e07451ce99a0a3438a233dfc67695d25ea9bfc Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Wed, 12 Apr 2023 15:33:23 +0530 Subject: [PATCH 189/466] Add unit test for asserting that first meta is migrated. --- .../PostsToOrdersMigrationControllerTest.php | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/plugins/woocommerce/tests/php/src/Database/Migrations/CustomOrderTable/PostsToOrdersMigrationControllerTest.php b/plugins/woocommerce/tests/php/src/Database/Migrations/CustomOrderTable/PostsToOrdersMigrationControllerTest.php index 1670e5444c3..22b2fbbccaa 100644 --- a/plugins/woocommerce/tests/php/src/Database/Migrations/CustomOrderTable/PostsToOrdersMigrationControllerTest.php +++ b/plugins/woocommerce/tests/php/src/Database/Migrations/CustomOrderTable/PostsToOrdersMigrationControllerTest.php @@ -746,4 +746,33 @@ WHERE order_id = {$order_id} AND meta_key = 'non_unique_key_1' AND meta_value in $this->assertEmpty( $errors ); } + + /** + * @testDox When there are mutli meta values for a supposed unique meta key, the first one is picked. + */ + public function test_first_value_is_picked_when_multi_value() { + global $wpdb; + $order = wc_get_order( OrderHelper::create_complex_wp_post_order() ); + $original_order_key = $order->get_order_key(); + + $this->assertNotEmpty( $original_order_key ); + + // Add a second order key. + add_post_meta( $order->get_id(), '_order_key', 'second_order_key_should_be_ignored' ); + + $this->sut->migrate_order( $order->get_id() ); + + $migrated_order_key = $wpdb->get_var( + $wpdb->prepare( + "SELECT order_key FROM {$wpdb->prefix}wc_order_operational_data WHERE order_id = %d", + $order->get_id() + ) + ); + + $this->assertEquals( $original_order_key, $migrated_order_key ); + + $errors = $this->sut->verify_migrated_orders( array( $order->get_id() ) ); + + $this->assertEmpty( $errors ); + } } From d5211bbaa65fda427503cefc11c90e94dd872177 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Wed, 12 Apr 2023 15:38:58 +0530 Subject: [PATCH 190/466] Use first meta value instead of last to be consistent with WP_Post. --- .../src/Database/Migrations/MetaToCustomTableMigrator.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php b/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php index 71b0d3022a4..c72ab967a3a 100644 --- a/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php +++ b/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php @@ -543,7 +543,11 @@ WHERE private function processs_and_sanitize_meta_data( array &$sanitized_entity_data, array &$error_records, array $meta_data ): void { foreach ( $meta_data as $datum ) { $column_schema = $this->meta_column_mapping[ $datum->meta_key ]; - $value = $this->validate_data( $datum->meta_value, $column_schema['type'] ); + if ( isset( $sanitized_entity_data[ $datum->entity_id ][ $column_schema['destination'] ] ) ) { + // We pick only the first meta if there are duplicates for a flat column, to be consistent with WP core behavior in handing duplicate meta which are marked as unique. + continue; + } + $value = $this->validate_data( $datum->meta_value, $column_schema['type'] ); if ( is_wp_error( $value ) ) { $error_records[ $datum->entity_id ][ $column_schema['destination'] ] = "{$value->get_error_code()}: {$value->get_error_message()}"; } else { From 2147d2abcf2a27ac30f8770a2fb7e444c711fb93 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Wed, 12 Apr 2023 15:39:44 +0530 Subject: [PATCH 191/466] Add changelog. --- plugins/woocommerce/changelog/fix-37660 | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 plugins/woocommerce/changelog/fix-37660 diff --git a/plugins/woocommerce/changelog/fix-37660 b/plugins/woocommerce/changelog/fix-37660 new file mode 100644 index 00000000000..19479c9b9bf --- /dev/null +++ b/plugins/woocommerce/changelog/fix-37660 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Use first meta value for HPOS migration when there are duplicates for flat column. From 4a5db60c2af550576f99e1b284c6e5438bae4d6c Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Wed, 12 Apr 2023 18:08:22 +0530 Subject: [PATCH 192/466] Adjust verification so that it only checks the first meta value. --- .../PostMetaToOrderMetaMigrator.php | 1 + .../PostToOrderAddressTableMigrator.php | 1 + .../PostToOrderOpTableMigrator.php | 1 + .../PostToOrderTableMigrator.php | 1 + .../Migrations/MetaToCustomTableMigrator.php | 80 +++++++++++++++---- 5 files changed, 67 insertions(+), 17 deletions(-) diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostMetaToOrderMetaMigrator.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostMetaToOrderMetaMigrator.php index a10720d2b4f..85a8ea5555f 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostMetaToOrderMetaMigrator.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostMetaToOrderMetaMigrator.php @@ -52,6 +52,7 @@ class PostMetaToOrderMetaMigrator extends MetaToMetaTableMigrator { 'meta' => array( 'table_name' => $wpdb->postmeta, 'entity_id_column' => 'post_id', + 'meta_id_column' => 'meta_id', 'meta_key_column' => 'meta_key', 'meta_value_column' => 'meta_value', ), diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderAddressTableMigrator.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderAddressTableMigrator.php index a9aba4bf6dd..526bd0a7da0 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderAddressTableMigrator.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderAddressTableMigrator.php @@ -56,6 +56,7 @@ class PostToOrderAddressTableMigrator extends MetaToCustomTableMigrator { ), 'meta' => array( 'table_name' => $wpdb->postmeta, + 'meta_id_column' => 'meta_id', 'meta_key_column' => 'meta_key', 'meta_value_column' => 'meta_value', 'entity_id_column' => 'post_id', diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderOpTableMigrator.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderOpTableMigrator.php index 05f22f6d1d0..995540baaf8 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderOpTableMigrator.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderOpTableMigrator.php @@ -40,6 +40,7 @@ class PostToOrderOpTableMigrator extends MetaToCustomTableMigrator { ), 'meta' => array( 'table_name' => $wpdb->postmeta, + 'meta_id_column' => 'meta_id', 'meta_key_column' => 'meta_key', 'meta_value_column' => 'meta_value', 'entity_id_column' => 'post_id', diff --git a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderTableMigrator.php b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderTableMigrator.php index e21085c62ba..8635eb36f7b 100644 --- a/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderTableMigrator.php +++ b/plugins/woocommerce/src/Database/Migrations/CustomOrderTable/PostToOrderTableMigrator.php @@ -39,6 +39,7 @@ class PostToOrderTableMigrator extends MetaToCustomTableMigrator { ), 'meta' => array( 'table_name' => $wpdb->postmeta, + 'meta_id_column' => 'meta_id', 'meta_key_column' => 'meta_key', 'meta_value_column' => 'meta_value', 'entity_id_column' => 'post_id', diff --git a/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php b/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php index c72ab967a3a..35829eef75e 100644 --- a/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php +++ b/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php @@ -614,7 +614,7 @@ WHERE $query = $this->build_verification_query( $source_ids ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query should already be prepared. $results = $wpdb->get_results( $query, ARRAY_A ); - + $results = $this->fill_source_metadata( $results, $source_ids ); return $this->verify_data( $results ); } @@ -627,19 +627,13 @@ WHERE */ protected function build_verification_query( $source_ids ) { $source_table = $this->schema_config['source']['entity']['table_name']; - $meta_table = $this->schema_config['source']['meta']['table_name']; $destination_table = $this->schema_config['destination']['table_name']; - $meta_entity_id_column = $this->schema_config['source']['meta']['entity_id_column']; - $meta_key_column = $this->schema_config['source']['meta']['meta_key_column']; - $meta_value_column = $this->schema_config['source']['meta']['meta_value_column']; $destination_source_rel_column = $this->schema_config['destination']['source_rel_column']; $source_destination_rel_column = $this->schema_config['source']['entity']['destination_rel_column']; - $source_meta_rel_column = $this->schema_config['source']['entity']['meta_rel_column']; $source_destination_join_clause = "$destination_table ON $destination_table.$destination_source_rel_column = $source_table.$source_destination_rel_column"; $meta_select_clauses = array(); - $meta_join_clauses = array(); $source_select_clauses = array(); $destination_select_clauses = array(); @@ -650,19 +644,11 @@ WHERE } foreach ( $this->meta_column_mapping as $meta_key => $schema ) { - $meta_table_alias = "meta_source_{$schema['destination']}"; - $meta_select_clauses[] = "$meta_table_alias.$meta_value_column AS $meta_table_alias"; - $meta_join_clauses[] = " -$meta_table $meta_table_alias ON - $meta_table_alias.$meta_entity_id_column = $source_table.$source_meta_rel_column AND - $meta_table_alias.$meta_key_column = '$meta_key' -"; $destination_select_clauses[] = "$destination_table.{$schema['destination']} as {$destination_table}_{$schema['destination']}"; } $select_clause = implode( ', ', array_merge( $source_select_clauses, $meta_select_clauses, $destination_select_clauses ) ); - $meta_join_clause = implode( ' LEFT JOIN ', $meta_join_clauses ); $where_clause = $this->get_where_clause_for_verification( $source_ids ); @@ -670,11 +656,71 @@ $meta_table $meta_table_alias ON SELECT $select_clause FROM $source_table LEFT JOIN $source_destination_join_clause - LEFT JOIN $meta_join_clause WHERE $where_clause "; } + /** + * Fill source metadata for given IDS for verification. + * + * @param array $source_ids List of source IDs. + * + * @return array List of source metadata. This will be in the following format: + * [ + * { + * $source_table_$source_column: $value, + * ..., + * $destination_table_$destination_column: $value, + * ... + * meta_source_{$destination_column_name1}: $meta_value, + * ... + * }, + * ... + * ] + */ + private function fill_source_metadata( $results, $source_ids ) { + global $wpdb; + $meta_table = $this->schema_config['source']['meta']['table_name']; + $meta_entity_id_column = $this->schema_config['source']['meta']['entity_id_column']; + $meta_key_column = $this->schema_config['source']['meta']['meta_key_column']; + $meta_value_column = $this->schema_config['source']['meta']['meta_value_column']; + $meta_id_column = $this->schema_config['source']['meta']['meta_id_column']; + $meta_columns = array_keys( $this->meta_column_mapping ); + + $meta_columns_placeholder = implode( ', ', array_fill( 0, count( $meta_columns ), '%s' ) ); + $source_ids_placeholder = implode( ', ', array_fill( 0, count( $source_ids ), '%d' ) ); + + $query = $wpdb->prepare( + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare + "SELECT $meta_entity_id_column as entity_id, $meta_key_column as meta_key, $meta_value_column as meta_value + FROM $meta_table + WHERE $meta_entity_id_column IN ($source_ids_placeholder) + AND $meta_key_column IN ($meta_columns_placeholder) + ORDER BY $meta_id_column ASC", + array_merge( $source_ids, $meta_columns ) + ); + + $meta_data = $wpdb->get_results( $query, ARRAY_A ); + $source_metadata_rows = array(); + foreach ( $meta_data as $meta_datum ) { + if ( ! isset( $source_metadata_rows[ $meta_datum['entity_id'] ] ) ) { + $source_metadata_rows[ $meta_datum['entity_id'] ] = array(); + } + $destination_column = $this->meta_column_mapping[ $meta_datum['meta_key'] ]['destination']; + $alias = "meta_source_{$destination_column}"; + if ( isset( $source_metadata_rows[ $meta_datum['entity_id'] ][ $alias ] ) ) { + // Only process first value, duplicate values mapping to flat columns are ignored to be consistent with WP core. + continue; + } + $source_metadata_rows[ $meta_datum['entity_id'] ][ $alias ] = $meta_datum['meta_value']; + } + foreach ( $results as $index => $result_row ) { + $source_id = $result_row[ $this->schema_config['source']['entity']['table_name'] . '_' . $this->schema_config['source']['entity']['primary_key'] ]; + $results[ $index ] = array_merge( $result_row, $source_metadata_rows[ $source_id ] ); + } + return $results; + } + /** * Helper function to generate where clause for fetching data for verification. * @@ -802,7 +848,7 @@ WHERE $where_clause $row[ $destination_alias ] = null; } } - if ( is_null( $row[ $alias ] ) ) { + if ( ! isset( $row[ $alias ] ) ) { $row[ $alias ] = $this->get_type_defaults( $schema['type'] ); } From e8363828f7a6a74b166caa55f94ef63e38116dcd Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Wed, 12 Apr 2023 18:40:19 +0530 Subject: [PATCH 193/466] Fixup to handle null data. --- .../src/Database/Migrations/MetaToCustomTableMigrator.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php b/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php index 35829eef75e..ee38fc669c5 100644 --- a/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php +++ b/plugins/woocommerce/src/Database/Migrations/MetaToCustomTableMigrator.php @@ -827,6 +827,9 @@ WHERE $where_clause * @return array Processed row. */ private function pre_process_row( $row, $schema, $alias, $destination_alias ) { + if ( ! isset( $row[ $alias ] ) ) { + $row[ $alias ] = $this->get_type_defaults( $schema['type'] ); + } if ( in_array( $schema['type'], array( 'int', 'decimal' ), true ) ) { if ( '' === $row[ $alias ] || null === $row[ $alias ] ) { $row[ $alias ] = 0; // $wpdb->prepare forces empty values to 0. @@ -848,10 +851,6 @@ WHERE $where_clause $row[ $destination_alias ] = null; } } - if ( ! isset( $row[ $alias ] ) ) { - $row[ $alias ] = $this->get_type_defaults( $schema['type'] ); - } - return $row; } From c8fcc51a5747f30a3d041398f278933ba0ed9245 Mon Sep 17 00:00:00 2001 From: Barry Hughes <3594411+barryhughes@users.noreply.github.com> Date: Wed, 12 Apr 2023 09:29:16 -0700 Subject: [PATCH 194/466] Add links to the main project README.md and DEVELOPMENT.md guides. Related internal discussion: p1681316323858719/1681312857.759079-slack-C04US2XTBM3 --- .github/CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 03c09011544..df2c01a88bf 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -21,6 +21,8 @@ If you have questions about the process to contribute code or want to discuss de ## Getting started +Please take a moment to review the [project readme](https://github.com/woocommerce/woocommerce/blob/trunk/README.md) and our [development notes](https://github.com/woocommerce/woocommerce/blob/trunk/DEVELOPMENT.md), which cover the basics needed to start working on this project. You may also be interested in the following resources: + - [How to set up WooCommerce development environment](https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment) - [Git Flow](https://github.com/woocommerce/woocommerce/wiki/WooCommerce-Git-Flow) - [Minification of SCSS and JS](https://github.com/woocommerce/woocommerce/wiki/Minification-of-SCSS-and-JS) From 1f4f485e2b785efe967c78a4b321ef78cc4a2ac3 Mon Sep 17 00:00:00 2001 From: barryhughes <3594411+barryhughes@users.noreply.github.com> Date: Wed, 12 Apr 2023 09:34:50 -0700 Subject: [PATCH 195/466] No changelog needed. --- plugins/woocommerce/changelog/update-contributor-notes | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 plugins/woocommerce/changelog/update-contributor-notes diff --git a/plugins/woocommerce/changelog/update-contributor-notes b/plugins/woocommerce/changelog/update-contributor-notes new file mode 100644 index 00000000000..c3da78ce7ce --- /dev/null +++ b/plugins/woocommerce/changelog/update-contributor-notes @@ -0,0 +1,5 @@ +Significance: patch +Type: tweak +Comment: This updates a document targeting developers, but there are no functional changes and a changelog entry is not required. + + From 2722ef4763d0fab378be0ac6f7e03af2f848b421 Mon Sep 17 00:00:00 2001 From: Fernando Marichal Date: Wed, 12 Apr 2023 14:30:15 -0300 Subject: [PATCH 196/466] Item controls for attribute creation are always visible (#37620) * Fix dropdown menu style * Move items * Add changelog * Remove class `select` --------- Co-authored-by: Fernando Marichal --- ...37159_item_controls_for_attribute_always_visible | 4 ++++ plugins/woocommerce/client/legacy/css/admin.scss | 13 +++---------- .../meta-boxes/views/html-product-attribute.php | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) create mode 100644 plugins/woocommerce/changelog/dev-37159_item_controls_for_attribute_always_visible diff --git a/plugins/woocommerce/changelog/dev-37159_item_controls_for_attribute_always_visible b/plugins/woocommerce/changelog/dev-37159_item_controls_for_attribute_always_visible new file mode 100644 index 00000000000..07536bf189a --- /dev/null +++ b/plugins/woocommerce/changelog/dev-37159_item_controls_for_attribute_always_visible @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Item controls for attribute creation are always visible diff --git a/plugins/woocommerce/client/legacy/css/admin.scss b/plugins/woocommerce/client/legacy/css/admin.scss index ad9a3e3d1cf..5548210a923 100644 --- a/plugins/woocommerce/client/legacy/css/admin.scss +++ b/plugins/woocommerce/client/legacy/css/admin.scss @@ -1130,6 +1130,7 @@ $default-line-height: 18px; } .toolbar-top { .button, + .attribute_taxonomy, .select2-container { margin: 1px; } @@ -5620,6 +5621,7 @@ img.help_tip { a.delete, a.edit { float: right; + margin-right: 12px; } a.delete { @@ -5655,7 +5657,7 @@ img.help_tip { .handlediv { background-position: 6px 5px !important; - visibility: hidden; + margin: 4px 0 -1px !important; height: 26px; } @@ -5671,7 +5673,6 @@ img.help_tip { a.delete, a.edit, - .handlediv, .sort { margin-top: 0.25em; } @@ -5684,14 +5685,6 @@ img.help_tip { } } - h3:hover, - &.ui-sortable-helper { - a.delete, - .handlediv { - visibility: visible; - } - } - table { width: 100%; position: relative; diff --git a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-attribute.php b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-attribute.php index 50efad423aa..80587895b71 100644 --- a/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-attribute.php +++ b/plugins/woocommerce/includes/admin/meta-boxes/views/html-product-attribute.php @@ -5,9 +5,9 @@ if ( ! defined( 'ABSPATH' ) ) { ?>

    -
    + get_name() !== '' ? wc_attribute_label( $attribute->get_name() ) : __( 'Custom attribute', 'woocommerce' ) ); ?>