From 6ea1d57c34309c8b1c82235a7fcb95973c37a927 Mon Sep 17 00:00:00 2001 From: jhnstn Date: Wed, 11 May 2022 14:30:19 -0400 Subject: [PATCH 01/32] Release script: Update react-native-editor version to 1.76.0 --- packages/react-native-aztec/package.json | 2 +- packages/react-native-bridge/package.json | 2 +- packages/react-native-editor/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native-aztec/package.json b/packages/react-native-aztec/package.json index 702f33d04d7204..1cb0d6af9cef32 100644 --- a/packages/react-native-aztec/package.json +++ b/packages/react-native-aztec/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-aztec", - "version": "1.75.0", + "version": "1.76.0", "description": "Aztec view for react-native.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-bridge/package.json b/packages/react-native-bridge/package.json index 4d6a09542ceefc..fa60ec4b09d7ee 100644 --- a/packages/react-native-bridge/package.json +++ b/packages/react-native-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-bridge", - "version": "1.75.0", + "version": "1.76.0", "description": "Native bridge library used to integrate the block editor into a native App.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index 71074238fe315e..417f3ed91782c3 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-editor", - "version": "1.75.0", + "version": "1.76.0", "description": "Mobile WordPress gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From bcddcbcbdb3f70fc42a58802d9634991f96023ad Mon Sep 17 00:00:00 2001 From: jhnstn Date: Wed, 11 May 2022 14:30:33 -0400 Subject: [PATCH 02/32] Release script: Update with changes from 'npm run core preios' --- .../ios/GutenbergDemo.xcodeproj/project.pbxproj | 4 ++-- packages/react-native-editor/ios/Podfile.lock | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/react-native-editor/ios/GutenbergDemo.xcodeproj/project.pbxproj b/packages/react-native-editor/ios/GutenbergDemo.xcodeproj/project.pbxproj index 65506b8af1e287..a08280d71e173f 100644 --- a/packages/react-native-editor/ios/GutenbergDemo.xcodeproj/project.pbxproj +++ b/packages/react-native-editor/ios/GutenbergDemo.xcodeproj/project.pbxproj @@ -764,7 +764,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 "; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -808,7 +808,7 @@ COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 "; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; diff --git a/packages/react-native-editor/ios/Podfile.lock b/packages/react-native-editor/ios/Podfile.lock index 90642b3975f923..facae9f7cbf87b 100644 --- a/packages/react-native-editor/ios/Podfile.lock +++ b/packages/react-native-editor/ios/Podfile.lock @@ -13,7 +13,7 @@ PODS: - ReactCommon/turbomodule/core (= 0.66.2) - fmt (6.2.1) - glog (0.3.5) - - Gutenberg (1.75.0): + - Gutenberg (1.76.0): - React-Core (= 0.66.2) - React-CoreModules (= 0.66.2) - React-RCTImage (= 0.66.2) @@ -337,7 +337,7 @@ PODS: - React-Core - RNSVG (9.13.6): - React-Core - - RNTAztecView (1.75.0): + - RNTAztecView (1.76.0): - React-Core - WordPress-Aztec-iOS (~> 1.19.8) - WordPress-Aztec-iOS (1.19.8) @@ -503,7 +503,7 @@ SPEC CHECKSUMS: FBReactNativeSpec: 18438b1c04ce502ed681cd19db3f4508964c082a fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 5337263514dd6f09803962437687240c5dc39aa4 - Gutenberg: a801215930ac72f0872814bfcce347532b6c20b6 + Gutenberg: 2398cf3b4c356206c72f83c216d3d01b624cc689 RCT-Folly: a21c126816d8025b547704b777a2ba552f3d9fa9 RCTRequired: 5e9e85f48da8dd447f5834ce14c6799ea8c7f41a RCTTypeSafety: aba333d04d88d1f954e93666a08d7ae57a87ab30 @@ -542,10 +542,10 @@ SPEC CHECKSUMS: RNReanimated: d87c75f1076bab3402d6cd0b7322be51d333d10e RNScreens: 953633729a42e23ad0c93574d676b361e3335e8b RNSVG: 36a7359c428dcb7c6bce1cc546fbfebe069809b0 - RNTAztecView: acf0844256727ca793923232f9eb93fc5fbb4343 + RNTAztecView: 0783a43eb5241e38587a52cb4eadc0ef2852607a WordPress-Aztec-iOS: 7d11d598f14c82c727c08b56bd35fbeb7dafb504 Yoga: 9a08effa851c1d8cc1647691895540bc168ea65f -PODFILE CHECKSUM: 563423e4045de5607d9ba4dd3a53595bb1a8c8cc +PODFILE CHECKSUM: d0a2d4714ee19a1821eb2a30029333be8d6a729f COCOAPODS: 1.10.1 From 678bd2f76732db68ed754b913c39015d44105fd2 Mon Sep 17 00:00:00 2001 From: jhnstn Date: Wed, 11 May 2022 15:13:42 -0400 Subject: [PATCH 03/32] Update Changelog --- packages/react-native-editor/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index ed8a8556a7d5eb..34b2157e09f909 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -11,6 +11,9 @@ For each user feature we should also add a importance categorization label to i ## Unreleased + +## 1.76.0 + - [**] [Buttons block] Fix Android-only issue related to displaying formatting buttons after closing the block settings [#40725] - [**] [Cover block] Improve color contrast between background and text [#40691] - [*] [Gallery block] Fix broken "Link To" settings and add "Image Size" settings [#40947] From 21b838c24f8ed4df6f6d6e250a40180b1af9993f Mon Sep 17 00:00:00 2001 From: Gerardo Date: Fri, 20 May 2022 12:48:33 +0200 Subject: [PATCH 04/32] Release script: Update react-native-editor version to 1.76.1 --- packages/react-native-aztec/package.json | 2 +- packages/react-native-bridge/package.json | 2 +- packages/react-native-editor/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native-aztec/package.json b/packages/react-native-aztec/package.json index 1cb0d6af9cef32..ee0f60f4566e58 100644 --- a/packages/react-native-aztec/package.json +++ b/packages/react-native-aztec/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-aztec", - "version": "1.76.0", + "version": "1.76.1", "description": "Aztec view for react-native.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-bridge/package.json b/packages/react-native-bridge/package.json index fa60ec4b09d7ee..655b7f42de9574 100644 --- a/packages/react-native-bridge/package.json +++ b/packages/react-native-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-bridge", - "version": "1.76.0", + "version": "1.76.1", "description": "Native bridge library used to integrate the block editor into a native App.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index 417f3ed91782c3..706c368919e23a 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-editor", - "version": "1.76.0", + "version": "1.76.1", "description": "Mobile WordPress gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From 7f79f5251cb94944f22c067d6098575022c13003 Mon Sep 17 00:00:00 2001 From: Gerardo Date: Fri, 20 May 2022 12:48:46 +0200 Subject: [PATCH 05/32] Release script: Update with changes from 'npm run core preios' --- packages/react-native-editor/ios/Podfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-native-editor/ios/Podfile.lock b/packages/react-native-editor/ios/Podfile.lock index facae9f7cbf87b..b2fd1726c26875 100644 --- a/packages/react-native-editor/ios/Podfile.lock +++ b/packages/react-native-editor/ios/Podfile.lock @@ -13,7 +13,7 @@ PODS: - ReactCommon/turbomodule/core (= 0.66.2) - fmt (6.2.1) - glog (0.3.5) - - Gutenberg (1.76.0): + - Gutenberg (1.76.1): - React-Core (= 0.66.2) - React-CoreModules (= 0.66.2) - React-RCTImage (= 0.66.2) @@ -337,7 +337,7 @@ PODS: - React-Core - RNSVG (9.13.6): - React-Core - - RNTAztecView (1.76.0): + - RNTAztecView (1.76.1): - React-Core - WordPress-Aztec-iOS (~> 1.19.8) - WordPress-Aztec-iOS (1.19.8) @@ -503,7 +503,7 @@ SPEC CHECKSUMS: FBReactNativeSpec: 18438b1c04ce502ed681cd19db3f4508964c082a fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 5337263514dd6f09803962437687240c5dc39aa4 - Gutenberg: 2398cf3b4c356206c72f83c216d3d01b624cc689 + Gutenberg: 9d1e70315e26fc3e9e8445b3f17c8ecb29f77e1b RCT-Folly: a21c126816d8025b547704b777a2ba552f3d9fa9 RCTRequired: 5e9e85f48da8dd447f5834ce14c6799ea8c7f41a RCTTypeSafety: aba333d04d88d1f954e93666a08d7ae57a87ab30 @@ -542,7 +542,7 @@ SPEC CHECKSUMS: RNReanimated: d87c75f1076bab3402d6cd0b7322be51d333d10e RNScreens: 953633729a42e23ad0c93574d676b361e3335e8b RNSVG: 36a7359c428dcb7c6bce1cc546fbfebe069809b0 - RNTAztecView: 0783a43eb5241e38587a52cb4eadc0ef2852607a + RNTAztecView: def74944705c4bef636fc653d11eaa1f49b64c22 WordPress-Aztec-iOS: 7d11d598f14c82c727c08b56bd35fbeb7dafb504 Yoga: 9a08effa851c1d8cc1647691895540bc168ea65f From 981471e673ba031f057e15d0b1a93689bef88272 Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco Date: Wed, 18 May 2022 10:52:53 +0200 Subject: [PATCH 06/32] [Mobile] - BlockList - Add internal onLayout from CellRendererComponent to BlockListItemCell (#41105) * Mobile - BlockList - Pass FlatList internal onLayout through CellRendererComponent * Mobile - Update onLayout naming for BlockListItemCell --- .../block-list/block-list-item-cell.native.js | 17 ++++++++++++----- .../src/components/block-list/index.native.js | 3 ++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block-list-item-cell.native.js b/packages/block-editor/src/components/block-list/block-list-item-cell.native.js index c399643a633996..e32d793af46d67 100644 --- a/packages/block-editor/src/components/block-list/block-list-item-cell.native.js +++ b/packages/block-editor/src/components/block-list/block-list-item-cell.native.js @@ -13,7 +13,7 @@ import { useEffect, useCallback } from '@wordpress/element'; */ import { useBlockListContext } from './block-list-context'; -function BlockListItemCell( { children, clientId, rootClientId } ) { +function BlockListItemCell( { children, clientId, rootClientId, onLayout } ) { const { blocksLayouts, updateBlocksLayouts } = useBlockListContext(); useEffect( () => { @@ -25,18 +25,25 @@ function BlockListItemCell( { children, clientId, rootClientId } ) { }; }, [] ); - const onLayout = useCallback( - ( { nativeEvent: { layout } } ) => { + const onCellLayout = useCallback( + ( event ) => { + const { + nativeEvent: { layout }, + } = event; updateBlocksLayouts( blocksLayouts, { clientId, rootClientId, ...layout, } ); + + if ( onLayout ) { + onLayout( event ); + } }, - [ clientId, rootClientId, updateBlocksLayouts ] + [ clientId, rootClientId, updateBlocksLayouts, onLayout ] ); - return { children }; + return { children }; } export default BlockListItemCell; diff --git a/packages/block-editor/src/components/block-list/index.native.js b/packages/block-editor/src/components/block-list/index.native.js index 3ec3fefc8207b3..a4248327589ed1 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -162,12 +162,13 @@ export class BlockList extends Component { return this.extraData; } - getCellRendererComponent( { children, item } ) { + getCellRendererComponent( { children, item, onLayout } ) { const { rootClientId } = this.props; return ( ); From 0087e3d1592e4c1c991162f6a434945f09cf53af Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco Date: Wed, 18 May 2022 11:05:19 +0200 Subject: [PATCH 07/32] [Mobile] - Fix Drag & Drop Chip positioning issue with RTL languages (#41053) * Mobile - Fix - Drag & drop chip issue with RTL languages * Mobile - BlockDraggable - Rename maxWidth variable to contentWidth * Mobile - BlockDraggable - Add custom exiting animation that uses the same functionality as ZoomOutEasyDown but customizing the translateX value taking into account RTL languages. --- .../block-draggable/index.native.js | 51 +++++++++++++++++-- .../src/components/block-list/index.native.js | 7 ++- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/block-draggable/index.native.js b/packages/block-editor/src/components/block-draggable/index.native.js index a52396db27fc43..ec76781d449fcc 100644 --- a/packages/block-editor/src/components/block-draggable/index.native.js +++ b/packages/block-editor/src/components/block-draggable/index.native.js @@ -2,6 +2,10 @@ * External dependencies */ import { AccessibilityInfo } from 'react-native'; +import { + useSafeAreaInsets, + useSafeAreaFrame, +} from 'react-native-safe-area-context'; import Animated, { runOnJS, runOnUI, @@ -11,7 +15,6 @@ import Animated, { withDelay, withTiming, ZoomInEasyDown, - ZoomOutEasyDown, } from 'react-native-reanimated'; /** @@ -61,10 +64,11 @@ const DEFAULT_IOS_LONG_PRESS_MIN_DURATION = * * @param {Object} props Component props. * @param {JSX.Element} props.children Children to be rendered. + * @param {boolean} props.isRTL Check if current locale is RTL. * * @return {Function} Render function that passes `onScroll` event handler. */ -const BlockDraggableWrapper = ( { children } ) => { +const BlockDraggableWrapper = ( { children, isRTL } ) => { const [ draggedBlockIcon, setDraggedBlockIcon ] = useState(); const { @@ -75,6 +79,10 @@ const BlockDraggableWrapper = ( { children } ) => { const { scrollRef } = useBlockListContext(); const animatedScrollRef = useAnimatedRef(); + const { left, right } = useSafeAreaInsets(); + const { width } = useSafeAreaFrame(); + const safeAreaOffset = left + right; + const contentWidth = width - safeAreaOffset; animatedScrollRef( scrollRef ); const scroll = { @@ -198,9 +206,16 @@ const BlockDraggableWrapper = ( { children } ) => { }; const chipDynamicStyles = useAnimatedStyle( () => { + const chipOffset = chip.width.value / 2; + const translateX = ! isRTL + ? chip.x.value - chipOffset + : -( contentWidth - ( chip.x.value + chipOffset ) ); + return { transform: [ - { translateX: chip.x.value - chip.width.value / 2 }, + { + translateX, + }, { translateY: chip.y.value - @@ -215,6 +230,34 @@ const BlockDraggableWrapper = ( { children } ) => { styles[ 'draggable-chip__wrapper' ], ]; + const exitingAnimation = ( { currentHeight, currentWidth } ) => { + 'worklet'; + const translateX = ! isRTL ? 0 : currentWidth * -1; + const duration = 150; + const animations = { + transform: [ + { + translateY: withTiming( currentHeight, { + duration, + } ), + }, + { + translateX: withTiming( translateX, { + duration, + } ), + }, + { scale: withTiming( 0, { duration } ) }, + ], + }; + const initialValues = { + transform: [ { translateY: 0 }, { translateX }, { scale: 1 } ], + }; + return { + initialValues, + animations, + }; + }; + return ( <> { { draggedBlockIcon && ( diff --git a/packages/block-editor/src/components/block-list/index.native.js b/packages/block-editor/src/components/block-list/index.native.js index a4248327589ed1..788ec01b59bb61 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -190,7 +190,7 @@ export class BlockList extends Component { } render() { - const { isRootList } = this.props; + const { isRootList, isRTL } = this.props; // Use of Context to propagate the main scroll ref to its children e.g InnerBlocks. const blockList = isRootList ? ( - + { ( { onScroll } ) => this.renderList( { onScroll } ) } @@ -439,6 +439,8 @@ export default compose( [ const isFloatingToolbarVisible = !! selectedBlockClientId && hasRootInnerBlocks; + const isRTL = getSettings().isRTL; + return { blockClientIds, blockCount, @@ -449,6 +451,7 @@ export default compose( [ isFloatingToolbarVisible, isStackedHorizontally, maxWidth, + isRTL, }; } ), From 5c6fe0d99b3f2d451e9020b9bebcb951cab5507c Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 19 May 2022 18:03:00 +0200 Subject: [PATCH 08/32] [RNMobile] Add drag & drop help guide in Help & Support screen (#40961) * Add drag & drop help guide * Update content of help screen related to moving blocks * Update drand-and-drop images * Update styles of heading component of help screen * Add HelpDetailBadge component This component will be rendered in `HelpDetailSectionHeadingText` component via the `badge` prop. * Add NEW badge to move blocks help section * Optimize drag-and-drop images * Add move-blocks icon to Help & Support screen * Update react-native-editor changelog * Add HelpSectionTitle component * Prevent rendering separator on last help topic item --- .../editor-help/help-section-title.native.js | 29 ++++++++++++ .../editor-help/help-topic-row.native.js | 4 +- .../editor-help/icon-move-blocks.native.js | 10 +++++ .../editor-help/images/drag-and-drop-dark.png | Bin 0 -> 2288 bytes .../images/drag-and-drop-dark@2x.png | Bin 0 -> 6381 bytes .../images/drag-and-drop-dark@3x.png | Bin 0 -> 12629 bytes .../images/drag-and-drop-light.png | Bin 0 -> 2990 bytes .../images/drag-and-drop-light@2x.png | Bin 0 -> 8441 bytes .../images/drag-and-drop-light@3x.png | Bin 0 -> 16898 bytes .../components/editor-help/index.native.js | 42 +++++++++--------- .../editor-help/move-blocks.native.js | 24 +++++++++- .../src/components/editor-help/style.scss | 40 +++++++++++++++-- .../editor-help/view-sections.native.js | 31 +++++++++---- 13 files changed, 142 insertions(+), 38 deletions(-) create mode 100644 packages/editor/src/components/editor-help/help-section-title.native.js create mode 100644 packages/editor/src/components/editor-help/icon-move-blocks.native.js create mode 100644 packages/editor/src/components/editor-help/images/drag-and-drop-dark.png create mode 100644 packages/editor/src/components/editor-help/images/drag-and-drop-dark@2x.png create mode 100644 packages/editor/src/components/editor-help/images/drag-and-drop-dark@3x.png create mode 100644 packages/editor/src/components/editor-help/images/drag-and-drop-light.png create mode 100644 packages/editor/src/components/editor-help/images/drag-and-drop-light@2x.png create mode 100644 packages/editor/src/components/editor-help/images/drag-and-drop-light@3x.png diff --git a/packages/editor/src/components/editor-help/help-section-title.native.js b/packages/editor/src/components/editor-help/help-section-title.native.js new file mode 100644 index 00000000000000..573d48d24fd6ce --- /dev/null +++ b/packages/editor/src/components/editor-help/help-section-title.native.js @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import { Text, View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { usePreferredColorSchemeStyle } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import styles from './style.scss'; + +const HelpSectionTitle = ( { children } ) => { + const helpSectionTitle = usePreferredColorSchemeStyle( + styles.helpSectionTitle, + styles.helpSectionTitleDark + ); + + return ( + + { children } + + ); +}; + +export default HelpSectionTitle; diff --git a/packages/editor/src/components/editor-help/help-topic-row.native.js b/packages/editor/src/components/editor-help/help-topic-row.native.js index e04ffae4a00b59..89495fc9a601d3 100644 --- a/packages/editor/src/components/editor-help/help-topic-row.native.js +++ b/packages/editor/src/components/editor-help/help-topic-row.native.js @@ -9,7 +9,7 @@ import { useNavigation } from '@react-navigation/native'; import { TextControl, Icon } from '@wordpress/components'; import { chevronRight } from '@wordpress/icons'; -const HelpTopicRow = ( { label, icon, screenName } ) => { +const HelpTopicRow = ( { label, icon, screenName, isLastItem } ) => { const navigation = useNavigation(); const openSubSheet = () => { @@ -18,7 +18,7 @@ const HelpTopicRow = ( { label, icon, screenName } ) => { return ( + + +); diff --git a/packages/editor/src/components/editor-help/images/drag-and-drop-dark.png b/packages/editor/src/components/editor-help/images/drag-and-drop-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..47188faf0a1a446bf45483fb40546762af380a94 GIT binary patch literal 2288 zcmV4E+Zo&ARr(mB_%U6GdemtIXO8nFfb=4Cr3v|R8&+zKtL!c zC^R%QP*6}&QBh4zO-@cuH#avQA0K;rd$hE)ySuxtuCA!4sE3D#r>Cd2wY8O%m6Vi} z7<|MQiQ5;6+ZKr07l_*zh}sv3*%pV{7KhjthSwH`))$7>7KPOoh13><(G`Kt6@SZt zfq@o)%oc*q7KGCmhu9a0*cOP{7Kz#ye8d-t+7^b`6@}Foh13;-(H4Quh=_<4fXfzw z&lZEy7KhgthuIg3+!uVqVPRp8j*g+Bp_`kVTU%QzD=SG!NlHpeJv}{7Pfs;9HBwSi zFE1}eMMXF`I6pr>K0ZDr^qaT<00)#wL_t(|0qokb4FfO;ML}a=|CLiX3#2fzl)DjL z34iei0000000000004m4Sw+X~&N3f)xpddTURTu@DfAGbg?^XNLL;=$&kj15*h2R=%R98>kI$@0APDZ$IO|OjG65nd*)|I3Oz(lgA2BS(MUELdlzLaPTspf=<<}yz%w+PI6Lqr1XGY%&}eRG z6QdQ;tj!V6)(cyMWCpYNDUySFtd5X5@Vj?s{#!X%)1fA$fZS3jmnn6{!vx6gLE`I~ z-&~|Daf+603aD+DWY*7+D=Hzjo7_J}-_QMxwF)me!lBtBlUoZNwadx+`7-TVa1`tF zeq*iGaF;Vmqe!qxUJ~rsY%9WlUI_aa#c@s1#O831$ z--iL5kwM3|l_b6P0{t52>BP98P1K$~AG*mfb13Ss=LH=xNc}*6D(Tn{4ZocH8*5}7 zs3&MC@@3dJl~@B;Z_r1J@BhT#$|>~*T~ISW122*~gO*4Ns3&Ko)EBg4{=vRk=r$9? zKsYOmIO2f!!Uti-|1YK(eE+9uU3SkW-KODq+Ax0_&8{7X3lN#S+pwdNGQaTvg~3giy*%^nG`I`RH_@BX+X|MngdNNEzBwCuo(^33{Sh= zk8lYrSfaw2PPZTvgMIXJ6=EO#jaj-KKeWwWhdOUlD~12xj*e`iV4)c;Z6*KDG7)uH z&?zfTWa$7$M}_mR5X zH4p<~DKj^JPCT4}wgvkB&jHEa$X+FYpez2Ex!lIi5mxuboj9qQ-p}|Kl?i}|b)dHZ z03Om%*Ju1o5)(BTu`L7*5QOIvrLNET7bTiBNMI@G08r+l8b9M-Qb~YqEoi3H_8I@K zGM5@e1Y)Um6w(sa_i4Mz5G25e#TBjesAK2}`gDTA%8HI4D0r&Q6SS#zx_eMK=Nv@%rW6C&Jf=$p6&l(H8Xl z?eX9o+xFq>w@Ghr$M7~XulASIeYEa~hyVd8C+ce*CVjqFy2WN~x3aq3Ms)LQANF)o z#1;UNUg68a&K^xkB?AEaIrl!M`dscL^r~+c(Mwoe# zKd9R?K>+g=;iIzE*g9kawW_nOfe0EOjNJ(;cnbhxVoW{e>3zYX%bp83-lKNMfIxtR zOBM(?L4ZMTCrdrcOZPrnX8_1GESfq7MF#*FV-4?+$I8;K);E>1!JTc|xL_AaGZ}%` zf!snC-dDF&)E9I%9byqon79x}X^Lz;dUqYheU`z(0X@vr$LOT6&erY_`+{~m zaESqar&HIeg;Rj=)(g{cQ8)ipR&v%E^My(o+hu;=sLM&?K3esv;;(dxT0W3j*X>V+ zl5a#TlX8@gq8eKVlfchF>rd%YnTtwyK8D6O{+X0T&9tOn?wOk7;zm>3!M>o)!cb+v za(rFp1E9kqHyK_YqLa3u>un`XXUMu#sn+XU(AA7(v@RwX~B$0W8-|UZS)EG3) zOXiT+S@;^eG23+w_>ns+J4rc1W6)^?DvXD!X&aSP%RA#jRHsjlv^(hf^PVdeT9WEN zE#;lNSDQ}Yb#IZaL0bi(-!(JmNtx<0pY<<5e{Y8l1ONa400jA453oWZglN&CMT-`V zXwjlYix!P&(V|6*7L91pqD6}qjcC!LMT-`VXwjlYix!P&(V{1IK$*qpr=k`B0000< KMNUMnLSTa97dR&X literal 0 HcmV?d00001 diff --git a/packages/editor/src/components/editor-help/images/drag-and-drop-dark@2x.png b/packages/editor/src/components/editor-help/images/drag-and-drop-dark@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2e928d2fcd98a92404430c107776e308b783f14e GIT binary patch literal 6381 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 n=2MRUeMPG%XvkN~nuq=u#n=!u literal 0 HcmV?d00001 diff --git a/packages/editor/src/components/editor-help/images/drag-and-drop-dark@3x.png b/packages/editor/src/components/editor-help/images/drag-and-drop-dark@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c49248c449ad26a21984ab2cd4a37e8719cb6ea8 GIT binary patch literal 12629 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 literal 0 HcmV?d00001 diff --git a/packages/editor/src/components/editor-help/images/drag-and-drop-light.png b/packages/editor/src/components/editor-help/images/drag-and-drop-light.png new file mode 100644 index 0000000000000000000000000000000000000000..844373d735f00e571d6a8863da23940e158d1bc7 GIT binary patch literal 2990 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 literal 0 HcmV?d00001 diff --git a/packages/editor/src/components/editor-help/images/drag-and-drop-light@3x.png b/packages/editor/src/components/editor-help/images/drag-and-drop-light@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f906e8f6f832261423fc91ae53e983fac3fea681 GIT binary patch literal 16898 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 literal 0 HcmV?d00001 diff --git a/packages/editor/src/components/editor-help/index.native.js b/packages/editor/src/components/editor-help/index.native.js index a4ce122aa95620..c9161b47863b4e 100644 --- a/packages/editor/src/components/editor-help/index.native.js +++ b/packages/editor/src/components/editor-help/index.native.js @@ -2,7 +2,7 @@ * External dependencies */ import { kebabCase } from 'lodash'; -import { Text, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native'; +import { SafeAreaView, ScrollView, StyleSheet, View } from 'react-native'; import { TransitionPresets } from '@react-navigation/stack'; /** @@ -14,16 +14,9 @@ import { PanelBody, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { - helpFilled, - plusCircleFilled, - alignJustify, - trash, - cog, -} from '@wordpress/icons'; +import { helpFilled, plusCircleFilled, trash, cog } from '@wordpress/icons'; import { useSelect } from '@wordpress/data'; import { store as editorStore } from '@wordpress/editor'; -import { usePreferredColorSchemeStyle } from '@wordpress/compose'; import { requestContactCustomerSupport, requestGotoCustomerSupportOptions, @@ -41,6 +34,8 @@ import AddBlocks from './add-blocks'; import MoveBlocks from './move-blocks'; import RemoveBlocks from './remove-blocks'; import CustomizeBlocks from './customize-blocks'; +import moveBlocksIcon from './icon-move-blocks'; +import HelpSectionTitle from './help-section-title'; const HELP_TOPICS = [ { @@ -53,7 +48,7 @@ const HELP_TOPICS = [ icon: plusCircleFilled, view: , }, - { label: __( 'Move blocks' ), icon: alignJustify, view: }, + { label: __( 'Move blocks' ), icon: moveBlocksIcon, view: }, { label: __( 'Remove blocks' ), icon: trash, view: }, { label: __( 'Customize blocks' ), @@ -67,11 +62,6 @@ function EditorHelpTopics( { close, isVisible, onClose } ) { postType: select( editorStore ).getEditedPostAttribute( 'type' ), } ) ); - const sectionTitle = usePreferredColorSchemeStyle( - styles.helpDetailSectionHeading, - styles.helpDetailSectionHeadingDark - ); - const title = postType === 'page' ? __( 'How to edit your page' ) @@ -130,15 +120,22 @@ function EditorHelpTopics( { close, isVisible, onClose } ) { } } > - + { __( 'The basics' ) } - + { /* Print out help topics. */ } { HELP_TOPICS.map( - ( { label, icon } ) => { + ( + { label, icon }, + index + ) => { const labelSlug = kebabCase( label ); + const isLastItem = + index === + HELP_TOPICS.length - + 1; return ( ); } ) } { - + { __( 'Get support' ) } - + } { { return ( <> + + + + + + diff --git a/packages/editor/src/components/editor-help/style.scss b/packages/editor/src/components/editor-help/style.scss index 4bac8875811b5d..8058f00381da95 100644 --- a/packages/editor/src/components/editor-help/style.scss +++ b/packages/editor/src/components/editor-help/style.scss @@ -31,6 +31,21 @@ padding: 0; } +.helpSectionTitleContainer { + margin-top: 24px; + margin-bottom: 16px; +} + +.helpSectionTitle { + color: $light-primary; + font-weight: 600; + font-size: 16px; +} + +.helpSectionTitleDark { + color: $dark-secondary; +} + .helpDetailContainer { padding: 0 16px; } @@ -62,14 +77,18 @@ } .helpDetailSectionHeading { + flex-direction: row; + align-items: center; +} + +.helpDetailSectionHeadingText { color: $light-primary; - font-weight: 600; + font-weight: 700; font-size: 16px; - margin-top: 24px; } -.helpDetailSectionHeadingDark { - color: $dark-secondary; +.helpDetailSectionHeadingTextDark { + color: $dark-primary; } .helpDetailBody { @@ -81,3 +100,16 @@ .helpDetailBodyDark { color: $dark-secondary; } + +.helpDetailBadgeContainer { + padding: 2px 6px; + margin-right: 8px; + border-radius: 6px; + background-color: #c9356e; +} + +.helpDetailBadgeText { + color: $white; + font-weight: 500; + font-size: 12px; +} diff --git a/packages/editor/src/components/editor-help/view-sections.native.js b/packages/editor/src/components/editor-help/view-sections.native.js index a1ed66e0525489..fefca22fa7e097 100644 --- a/packages/editor/src/components/editor-help/view-sections.native.js +++ b/packages/editor/src/components/editor-help/view-sections.native.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { Text, Image } from 'react-native'; +import { Text, Image, View } from 'react-native'; /** * WordPress dependencies @@ -28,15 +28,22 @@ export const HelpDetailBodyText = ( { text } ) => { ); }; -export const HelpDetailSectionHeadingText = ( { text } ) => { - const headingStyle = usePreferredColorSchemeStyle( - styles.helpDetailSectionHeading, - styles.helpDetailSectionHeadingDark +export const HelpDetailSectionHeadingText = ( { text, badge } ) => { + const headingTextStyle = usePreferredColorSchemeStyle( + styles.helpDetailSectionHeadingText, + styles.helpDetailSectionHeadingTextDark ); return ( - - { text } - + + { badge && } + + { text } + + ); }; @@ -62,3 +69,11 @@ export const HelpDetailImage = ( { /> ); }; + +const HelpDetailBadge = ( { text } ) => { + return ( + + { text } + + ); +}; From 09d4368f2a81c604baaecca06a7b31e0cd777ba1 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 20 May 2022 12:28:28 +0200 Subject: [PATCH 09/32] [RNMobile] Fix drag mode not being enabled when long-pressing over Shortcode block (#41155) * Add prop for disabling suggestions button * Use allowed formats in format types calculation * Add RichText version to PlainText component * Use experimental version of PlainText in Shortcode block * Add disableAutocorrection prop to RichText * Disable autocorrection in Shortcode block * Update PlainText props in Shortcode block * Use pre as tagName in PlainText * Rename replaceLineBreaks function * Update shortcode block unit tests * Prevent text input focus when selecting Shortcode block * Force text color in Shortcode block * Remove tagName prop from PlainText component --- .../src/components/plain-text/index.native.js | 72 ++++++++++++-- .../src/components/rich-text/index.js | 2 + .../src/components/rich-text/index.native.js | 4 + .../src/shortcode/edit.native.js | 44 ++++++--- .../src/shortcode/style.native.scss | 15 ++- .../test/__snapshots__/edit.native.js.snap | 9 ++ .../src/shortcode/test/edit.native.js | 98 +++++++++++-------- .../ReactNativeAztec/ReactAztecManager.java | 11 +++ .../ios/RNTAztecView/RCTAztecView.swift | 6 ++ .../ios/RNTAztecView/RCTAztecViewManager.m | 1 + .../rich-text/src/component/index.native.js | 27 +++-- test/native/__mocks__/styleMock.js | 3 + 12 files changed, 217 insertions(+), 75 deletions(-) create mode 100644 packages/block-library/src/shortcode/test/__snapshots__/edit.native.js.snap diff --git a/packages/block-editor/src/components/plain-text/index.native.js b/packages/block-editor/src/components/plain-text/index.native.js index 13d2bbf851837b..6c8259ceae1600 100644 --- a/packages/block-editor/src/components/plain-text/index.native.js +++ b/packages/block-editor/src/components/plain-text/index.native.js @@ -7,7 +7,7 @@ import { TextInput, Platform, Dimensions } from 'react-native'; * WordPress dependencies */ import { Component } from '@wordpress/element'; -import { getPxFromCssUnit } from '@wordpress/block-editor'; +import { RichText, getPxFromCssUnit } from '@wordpress/block-editor'; /** * Internal dependencies @@ -18,6 +18,9 @@ export default class PlainText extends Component { constructor() { super( ...arguments ); this.isAndroid = Platform.OS === 'android'; + + this.onChangeTextInput = this.onChangeTextInput.bind( this ); + this.onChangeRichText = this.onChangeRichText.bind( this ); } componentDidMount() { @@ -44,7 +47,7 @@ export default class PlainText extends Component { componentDidUpdate( prevProps ) { if ( ! this.props.isSelected && prevProps.isSelected ) { - this._input.blur(); + this._input?.blur(); } } @@ -55,11 +58,11 @@ export default class PlainText extends Component { } focus() { - this._input.focus(); + this._input?.focus(); } blur() { - this._input.blur(); + this._input?.blur(); } getFontSize() { @@ -79,20 +82,73 @@ export default class PlainText extends Component { }; } + replaceLineBreakTags( value ) { + return value?.replace( RegExp( '
', 'gim' ), '\n' ); + } + + onChangeTextInput( event ) { + const { onChange } = this.props; + onChange( event.nativeEvent.text ); + } + + onChangeRichText( value ) { + const { onChange } = this.props; + // The
tags have to be replaced with new line characters + // as the content of plain text shouldn't contain HTML tags. + onChange( this.replaceLineBreakTags( value ) ); + } + render() { - const { style } = this.props; + const { + style, + __experimentalVersion, + onFocus, + ...otherProps + } = this.props; const textStyles = [ style || styles[ 'block-editor-plain-text' ], this.getFontSize(), ]; + if ( __experimentalVersion === 2 ) { + const disableFormattingProps = { + withoutInteractiveFormatting: true, + disableEditingMenu: true, + __unstableDisableFormats: true, + disableSuggestions: true, + }; + + const forcePlainTextProps = { + preserveWhiteSpace: true, + __unstablePastePlainText: true, + multiline: false, + }; + + const fontProps = { + fontFamily: style?.fontFamily, + fontSize: style?.fontSize, + fontWeight: style?.fontWeight, + }; + + return ( + + ); + } + return ( ( this._input = x ) } - onChange={ ( event ) => { - this.props.onChange( event.nativeEvent.text ); - } } + onChange={ this.onChangeTextInput } onFocus={ this.props.onFocus } // Always assign onFocus as a props. onBlur={ this.props.onBlur } // Always assign onBlur as a props. fontFamily={ diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index d30fa8eae5e92d..eacd225eeb091e 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -76,6 +76,8 @@ function removeNativeProps( props ) { 'minWidth', 'maxWidth', 'setRef', + 'disableSuggestions', + 'disableAutocorrection', ] ); } diff --git a/packages/block-editor/src/components/rich-text/index.native.js b/packages/block-editor/src/components/rich-text/index.native.js index 2c24022c8e9749..0232b68a2a671c 100644 --- a/packages/block-editor/src/components/rich-text/index.native.js +++ b/packages/block-editor/src/components/rich-text/index.native.js @@ -111,6 +111,8 @@ function RichTextWrapper( maxWidth, onBlur, setRef, + disableSuggestions, + disableAutocorrection, ...props }, forwardedRef @@ -635,6 +637,8 @@ function RichTextWrapper( maxWidth={ maxWidth } onBlur={ onBlur } setRef={ setRef } + disableSuggestions={ disableSuggestions } + disableAutocorrection={ disableAutocorrection } // Props to be set on the editable container are destructured on the // element itself for web (see below), but passed through rich text // for native. diff --git a/packages/block-library/src/shortcode/edit.native.js b/packages/block-library/src/shortcode/edit.native.js index 778254784682f1..044ad1d23877f8 100644 --- a/packages/block-library/src/shortcode/edit.native.js +++ b/packages/block-library/src/shortcode/edit.native.js @@ -9,6 +9,7 @@ import { View, Text } from 'react-native'; import { __ } from '@wordpress/i18n'; import { PlainText } from '@wordpress/block-editor'; import { withPreferredColorScheme } from '@wordpress/compose'; +import { useCallback } from '@wordpress/element'; /** * Internal dependencies @@ -23,11 +24,16 @@ export function ShortcodeEdit( props ) { onFocus, onBlur, getStylesFromColorScheme, + blockWidth, } = props; const titleStyle = getStylesFromColorScheme( styles.blockTitle, styles.blockTitleDark ); + const shortcodeContainerStyle = getStylesFromColorScheme( + styles.blockShortcodeContainer, + styles.blockShortcodeContainerDark + ); const shortcodeStyle = getStylesFromColorScheme( styles.blockShortcode, styles.blockShortcodeDark @@ -37,24 +43,32 @@ export function ShortcodeEdit( props ) { styles.placeholderDark ); + const maxWidth = + blockWidth - + shortcodeContainerStyle.paddingLeft + + shortcodeContainerStyle.paddingRight; + + const onChange = useCallback( ( text ) => setAttributes( { text } ), [ + setAttributes, + ] ); + return ( { __( 'Shortcode' ) } - setAttributes( { text } ) } - placeholder={ __( 'Add a shortcode…' ) } - aria-label={ __( 'Shortcode' ) } - isSelected={ props.isSelected } - onFocus={ onFocus } - onBlur={ onBlur } - autoCorrect={ false } - autoComplete="off" - placeholderTextColor={ placeholderStyle.color } - /> + <View style={ shortcodeContainerStyle }> + <PlainText + __experimentalVersion={ 2 } + value={ attributes.text } + style={ shortcodeStyle } + onChange={ onChange } + placeholder={ __( 'Add a shortcode…' ) } + onFocus={ onFocus } + onBlur={ onBlur } + placeholderTextColor={ placeholderStyle.color } + maxWidth={ maxWidth } + disableAutocorrection + /> + </View> </View> ); } diff --git a/packages/block-library/src/shortcode/style.native.scss b/packages/block-library/src/shortcode/style.native.scss index ed7d235960abdd..b828e7ff28f7a9 100644 --- a/packages/block-library/src/shortcode/style.native.scss +++ b/packages/block-library/src/shortcode/style.native.scss @@ -11,18 +11,25 @@ color: $gray-50; } +.blockShortcodeContainer { + padding: 12px; + border-radius: 4px; + background-color: $gray-light; +} + +.blockShortcodeContainerDark { + background-color: $gray-100; +} + .blockShortcode { font-family: $default-monospace-font; font-weight: 400; font-size: 14px; - padding: 12px; - border-radius: 4px; - background-color: $gray-light; + color: $gray-900; } .blockShortcodeDark { color: $white; - background-color: $gray-100; } .placeholder { diff --git a/packages/block-library/src/shortcode/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/shortcode/test/__snapshots__/edit.native.js.snap new file mode 100644 index 00000000000000..12d5722a68c39d --- /dev/null +++ b/packages/block-library/src/shortcode/test/__snapshots__/edit.native.js.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Shortcode block edits content 1`] = ` +"<!-- wp:shortcode --> +[youtube https://www.youtube.com/watch?v=ssfHW5lwFZg] +<!-- /wp:shortcode -->" +`; + +exports[`Shortcode block inserts block 1`] = `"<!-- wp:shortcode /-->"`; diff --git a/packages/block-library/src/shortcode/test/edit.native.js b/packages/block-library/src/shortcode/test/edit.native.js index c6aa9261b6d9f5..f6c9ce705f5ad3 100644 --- a/packages/block-library/src/shortcode/test/edit.native.js +++ b/packages/block-library/src/shortcode/test/edit.native.js @@ -1,58 +1,76 @@ /** * External dependencies */ -import renderer from 'react-test-renderer'; -import { TextInput } from 'react-native'; +import { + getEditorHtml, + initializeEditor, + fireEvent, + waitFor, +} from 'test/helpers'; /** * WordPress dependencies */ -import { BlockEdit } from '@wordpress/block-editor'; -import { registerBlockType, unregisterBlockType } from '@wordpress/blocks'; +import { getBlockTypes, unregisterBlockType } from '@wordpress/blocks'; +import { registerCoreBlocks } from '@wordpress/block-library'; -/** - * Internal dependencies - */ -import { metadata, settings, name } from '../index'; +beforeAll( () => { + // Register all core blocks + registerCoreBlocks(); +} ); + +afterAll( () => { + // Clean up registered blocks + getBlockTypes().forEach( ( block ) => { + unregisterBlockType( block.name ); + } ); +} ); + +describe( 'Shortcode block', () => { + it( 'inserts block', async () => { + const { + getByA11yLabel, + getByTestId, + getByText, + } = await initializeEditor(); -const Shortcode = ( { clientId, ...props } ) => ( - <BlockEdit name={ name } clientId={ clientId || 0 } { ...props } /> -); + fireEvent.press( getByA11yLabel( 'Add block' ) ); -describe( 'Shortcode', () => { - beforeAll( () => { - registerBlockType( name, { - ...metadata, - ...settings, + const blockList = getByTestId( 'InserterUI-Blocks' ); + // onScroll event used to force the FlatList to render all items + fireEvent.scroll( blockList, { + nativeEvent: { + contentOffset: { y: 0, x: 0 }, + contentSize: { width: 100, height: 100 }, + layoutMeasurement: { width: 100, height: 100 }, + }, } ); - } ); - afterAll( () => { - unregisterBlockType( name ); - } ); + fireEvent.press( await waitFor( () => getByText( 'Shortcode' ) ) ); - it( 'renders without crashing', () => { - const component = renderer.create( - <Shortcode attributes={ { text: '' } } /> - ); - const rendered = component.toJSON(); - expect( rendered ).toBeTruthy(); + expect( getByA11yLabel( /Shortcode Block\. Row 1/ ) ).toBeVisible(); + expect( getEditorHtml() ).toMatchSnapshot(); } ); - it( 'renders given text without crashing', () => { - const component = renderer.create( - <Shortcode - attributes={ { - text: - '[youtube https://www.youtube.com/watch?v=ssfHW5lwFZg]', - } } - /> - ); - const testInstance = component.root; - const textInput = testInstance.findByType( TextInput ); - expect( textInput ).toBeTruthy(); - expect( textInput.props.value ).toBe( - '[youtube https://www.youtube.com/watch?v=ssfHW5lwFZg]' + it( 'edits content', async () => { + const { getByA11yLabel, getByPlaceholderText } = await initializeEditor( + { + initialHtml: '<!-- wp:shortcode /-->', + } ); + const shortcodeBlock = getByA11yLabel( /Shortcode Block\. Row 1/ ); + fireEvent.press( shortcodeBlock ); + + const textField = getByPlaceholderText( 'Add a shortcode…' ); + fireEvent( textField, 'focus' ); + fireEvent( textField, 'onChange', { + nativeEvent: { + eventCount: 1, + target: undefined, + text: '[youtube https://www.youtube.com/watch?v=ssfHW5lwFZg]', + }, + } ); + + expect( getEditorHtml() ).toMatchSnapshot(); } ); } ); diff --git a/packages/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java b/packages/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java index 52dac695bc9973..1e3e90c3a48442 100644 --- a/packages/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java +++ b/packages/react-native-aztec/android/src/main/java/org/wordpress/mobile/ReactNativeAztec/ReactAztecManager.java @@ -13,6 +13,7 @@ import android.os.Handler; import android.os.Looper; import android.text.Editable; +import android.text.InputType; import android.text.Layout; import android.text.TextUtils; import android.text.TextWatcher; @@ -617,6 +618,16 @@ public void setShouldDeleteEnter(final ReactAztecText view, boolean shouldDelete view.shouldDeleteEnter = shouldDeleteEnter; } + @ReactProp(name = "disableAutocorrection", defaultBoolean = false) + public void disableAutocorrection(final ReactAztecText view, boolean disable) { + if (disable) { + view.setInputType((view.getInputType() & ~InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + } + else { + view.setInputType((view.getInputType() & ~InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); + } + } + @Override public Map<String, Integer> getCommandsMap() { return MapBuilder.<String, Integer>builder() diff --git a/packages/react-native-aztec/ios/RNTAztecView/RCTAztecView.swift b/packages/react-native-aztec/ios/RNTAztecView/RCTAztecView.swift index 5413041b6ac6ff..cc87ef29e9704b 100644 --- a/packages/react-native-aztec/ios/RNTAztecView/RCTAztecView.swift +++ b/packages/react-native-aztec/ios/RNTAztecView/RCTAztecView.swift @@ -31,6 +31,12 @@ class RCTAztecView: Aztec.TextView { } } + @objc var disableAutocorrection: Bool = false { + didSet { + autocorrectionType = disableAutocorrection ? .no : .default + } + } + override var textAlignment: NSTextAlignment { set { super.textAlignment = newValue diff --git a/packages/react-native-aztec/ios/RNTAztecView/RCTAztecViewManager.m b/packages/react-native-aztec/ios/RNTAztecView/RCTAztecViewManager.m index bad6dd8abf7680..e8038ab17a7044 100644 --- a/packages/react-native-aztec/ios/RNTAztecView/RCTAztecViewManager.m +++ b/packages/react-native-aztec/ios/RNTAztecView/RCTAztecViewManager.m @@ -31,6 +31,7 @@ @interface RCT_EXTERN_MODULE(RCTAztecViewManager, NSObject) RCT_EXPORT_VIEW_PROPERTY(lineHeight, CGFloat) RCT_EXPORT_VIEW_PROPERTY(disableEditingMenu, BOOL) +RCT_EXPORT_VIEW_PROPERTY(disableAutocorrection, BOOL) RCT_REMAP_VIEW_PROPERTY(textAlign, textAlignment, NSTextAlignment) RCT_REMAP_VIEW_PROPERTY(selectionColor, tintColor, UIColor) diff --git a/packages/rich-text/src/component/index.native.js b/packages/rich-text/src/component/index.native.js index f460019df8b3ce..4fadc90b8d4baa 100644 --- a/packages/rich-text/src/component/index.native.js +++ b/packages/rich-text/src/component/index.native.js @@ -1050,6 +1050,7 @@ export class RichText extends Component { baseGlobalStyles, selectionStart, selectionEnd, + disableSuggestions, } = this.props; const { currentFontSize } = this.state; @@ -1220,6 +1221,7 @@ export class RichText extends Component { minWidth={ minWidth } id={ this.props.id } selectionColor={ this.props.selectionColor } + disableAutocorrection={ this.props.disableAutocorrection } /> { isSelected && ( <> @@ -1230,11 +1232,13 @@ export class RichText extends Component { onChange={ this.onFormatChange } onFocus={ () => {} } /> - <BlockFormatControls> - <ToolbarButtonWithOptions - options={ this.suggestionOptions() } - /> - </BlockFormatControls> + { ! disableSuggestions && ( + <BlockFormatControls> + <ToolbarButtonWithOptions + options={ this.suggestionOptions() } + /> + </BlockFormatControls> + ) } </> ) } </View> @@ -1249,10 +1253,17 @@ RichText.defaultProps = { }; const withFormatTypes = ( WrappedComponent ) => ( props ) => { + const { + clientId, + identifier, + withoutInteractiveFormatting, + allowedFormats, + } = props; const { formatTypes } = useFormatTypes( { - clientId: props.clientId, - identifier: props.identifier, - withoutInteractiveFormatting: props.withoutInteractiveFormatting, + clientId, + identifier, + withoutInteractiveFormatting, + allowedFormats, } ); return <WrappedComponent { ...props } formatTypes={ formatTypes } />; diff --git a/test/native/__mocks__/styleMock.js b/test/native/__mocks__/styleMock.js index 4723663203954f..11f58d79b1afcd 100644 --- a/test/native/__mocks__/styleMock.js +++ b/test/native/__mocks__/styleMock.js @@ -162,4 +162,7 @@ module.exports = { 'dropping-insertion-point': { height: 3, }, + blockShortcodeContainer: { + padding: 12, + }, }; From c951e5965ed5be70e011314f7578e0ce5558f8b0 Mon Sep 17 00:00:00 2001 From: Gerardo <gerardo.pacheco@automattic.com> Date: Fri, 20 May 2022 13:22:11 +0200 Subject: [PATCH 10/32] Mobile - Update changelog --- packages/react-native-editor/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 34b2157e09f909..ac9774f6013363 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -11,6 +11,12 @@ For each user feature we should also add a importance categorization label to i ## Unreleased +## 1.76.1 + +- [*] BlockList - Add internal onLayout from CellRendererComponent to BlockListItemCell [#41105] +- [*] Fix Drag & Drop Chip positioning issue with RTL languages [#41053] +- [*] Add drag & drop help guide in Help & Support screen [#40961] +- [**] Fix drag mode not being enabled when long-pressing over Shortcode block [#41155] ## 1.76.0 From d0b2cea01f9d12ae384ea8f1c9d32c0664cad5b8 Mon Sep 17 00:00:00 2001 From: Carlos Garcia <fluiddot@gmail.com> Date: Fri, 20 May 2022 16:12:43 +0200 Subject: [PATCH 11/32] Translate NEW badge in Move blocks help screen --- .../editor/src/components/editor-help/move-blocks.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/components/editor-help/move-blocks.native.js b/packages/editor/src/components/editor-help/move-blocks.native.js index 0bfdd64eb2253e..44e9f35f6ec375 100644 --- a/packages/editor/src/components/editor-help/move-blocks.native.js +++ b/packages/editor/src/components/editor-help/move-blocks.native.js @@ -28,7 +28,7 @@ const MoveBlocks = () => { <View style={ styles.helpDetailContainer }> <HelpDetailSectionHeadingText text={ __( 'Drag & drop' ) } - badge="NEW" + badge={ __( 'NEW' ) } /> <HelpDetailBodyText text={ __( From 132bc3abcbc839eed5c15a62fe528efeb78c653f Mon Sep 17 00:00:00 2001 From: Siobhan <siobhan@automattic.com> Date: Tue, 24 May 2022 10:47:23 +0100 Subject: [PATCH 12/32] Release script: Update react-native-editor version to 1.77.0 --- packages/react-native-aztec/package.json | 2 +- packages/react-native-bridge/package.json | 2 +- packages/react-native-editor/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native-aztec/package.json b/packages/react-native-aztec/package.json index ee0f60f4566e58..1d6c079cfbf0c2 100644 --- a/packages/react-native-aztec/package.json +++ b/packages/react-native-aztec/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-aztec", - "version": "1.76.1", + "version": "1.77.0", "description": "Aztec view for react-native.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-bridge/package.json b/packages/react-native-bridge/package.json index 655b7f42de9574..525cbcfbc2465f 100644 --- a/packages/react-native-bridge/package.json +++ b/packages/react-native-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-bridge", - "version": "1.76.1", + "version": "1.77.0", "description": "Native bridge library used to integrate the block editor into a native App.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index 706c368919e23a..c02f10c28a5ef3 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-editor", - "version": "1.76.1", + "version": "1.77.0", "description": "Mobile WordPress gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From cb4105f5f6e0342430a2e987f8c0c12bbf7ff034 Mon Sep 17 00:00:00 2001 From: Siobhan <siobhan@automattic.com> Date: Tue, 24 May 2022 10:48:01 +0100 Subject: [PATCH 13/32] Release script: Update with changes from 'npm run core preios' --- packages/react-native-editor/ios/Podfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-native-editor/ios/Podfile.lock b/packages/react-native-editor/ios/Podfile.lock index b2fd1726c26875..d12d4b360b34c6 100644 --- a/packages/react-native-editor/ios/Podfile.lock +++ b/packages/react-native-editor/ios/Podfile.lock @@ -13,7 +13,7 @@ PODS: - ReactCommon/turbomodule/core (= 0.66.2) - fmt (6.2.1) - glog (0.3.5) - - Gutenberg (1.76.1): + - Gutenberg (1.77.0): - React-Core (= 0.66.2) - React-CoreModules (= 0.66.2) - React-RCTImage (= 0.66.2) @@ -337,7 +337,7 @@ PODS: - React-Core - RNSVG (9.13.6): - React-Core - - RNTAztecView (1.76.1): + - RNTAztecView (1.77.0): - React-Core - WordPress-Aztec-iOS (~> 1.19.8) - WordPress-Aztec-iOS (1.19.8) @@ -503,7 +503,7 @@ SPEC CHECKSUMS: FBReactNativeSpec: 18438b1c04ce502ed681cd19db3f4508964c082a fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 5337263514dd6f09803962437687240c5dc39aa4 - Gutenberg: 9d1e70315e26fc3e9e8445b3f17c8ecb29f77e1b + Gutenberg: 7f0a288f26e36807c8e27f97b3a6ff929310eea3 RCT-Folly: a21c126816d8025b547704b777a2ba552f3d9fa9 RCTRequired: 5e9e85f48da8dd447f5834ce14c6799ea8c7f41a RCTTypeSafety: aba333d04d88d1f954e93666a08d7ae57a87ab30 @@ -542,7 +542,7 @@ SPEC CHECKSUMS: RNReanimated: d87c75f1076bab3402d6cd0b7322be51d333d10e RNScreens: 953633729a42e23ad0c93574d676b361e3335e8b RNSVG: 36a7359c428dcb7c6bce1cc546fbfebe069809b0 - RNTAztecView: def74944705c4bef636fc653d11eaa1f49b64c22 + RNTAztecView: 2524811d1168d1f5148220ec8ea3c012ec8f059c WordPress-Aztec-iOS: 7d11d598f14c82c727c08b56bd35fbeb7dafb504 Yoga: 9a08effa851c1d8cc1647691895540bc168ea65f From 639598f6f5ab6c884ee415200bea92915ab1f305 Mon Sep 17 00:00:00 2001 From: Siobhan Bamber <siobhan@automattic.com> Date: Fri, 13 May 2022 17:33:45 +0100 Subject: [PATCH 14/32] [RNMobile] Improve text read by screen readers for BottomSheetSelectControl (#41036) Improves the text that's read by screen readers by the BottomSheetSelectControl component, adding extra context and making its purpose clearer. --- .../mobile/bottom-sheet-select-control/index.native.js | 9 +++++++-- packages/react-native-editor/CHANGELOG.md | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/components/src/mobile/bottom-sheet-select-control/index.native.js b/packages/components/src/mobile/bottom-sheet-select-control/index.native.js index 6d88c349b78340..cc0a340104f86c 100644 --- a/packages/components/src/mobile/bottom-sheet-select-control/index.native.js +++ b/packages/components/src/mobile/bottom-sheet-select-control/index.native.js @@ -57,11 +57,16 @@ const BottomSheetSelectControl = ( { value={ selectedOption.label } onPress={ openSubSheet } accessibilityRole={ 'button' } - accessibilityLabel={ selectedOption.label } + accessibilityLabel={ sprintf( + // translators: %1$s: Select control button label e.g. "Button width". %2$s: Select control option value e.g: "Auto, 25%". + __( '%1$s. Currently selected: %2$s' ), + label, + selectedOption.label + ) } accessibilityHint={ sprintf( // translators: %s: Select control button label e.g. "Button width" __( 'Navigates to select %s' ), - selectedOption.label + label ) } > <Icon icon={ chevronRight }></Icon> diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index ac9774f6013363..9b3a6cf719b568 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -11,6 +11,10 @@ For each user feature we should also add a importance categorization label to i ## Unreleased +## 1.77.0 + +- [*] [a11y] Improve text read by screen readers for BottomSheetSelectControl [#41036] + ## 1.76.1 - [*] BlockList - Add internal onLayout from CellRendererComponent to BlockListItemCell [#41105] From 3fe5683d95e804b11892f5c270c8527297231b58 Mon Sep 17 00:00:00 2001 From: Derek Blank <derekpblank@gmail.com> Date: Tue, 24 May 2022 00:24:30 +1000 Subject: [PATCH 15/32] [RNMobile] Add 'Insert from URL' option to Image block (#40334) * Add 'Insert from URL' option to Video and Image blocks * Update code style from linting * Improve test cases for Media Upload capture options * Fix whitespace issue * Update Media Upload option tests to be asynchronous * Update native image block to use correct image URL * Add error handling for invalid URLs to native Image block * Clear invalid URL error on Image URL success * Fix synchronicity of Media Upload option tests * Add check for URL handler to native Image block picker options * Update code style * Remove Video block from urlSource options Why: to be introduced in a later PR * Remove URL option from Video block for Media Upload test * Use Notice snackbar for native Image block error handling * Update Image/Media Upload code style and helpers * Use getImage to determine if URL is a valid image within Image block * Add loading indicator and isURL check to native Image block URL behavior * Add loading indicator to native Image block media placeholder * Fix whitespace issue in native Image block code style * Reuse native Image block loading indicator * Use undefined dimension attributes for the native Image block URL behavior Co-authored-by: Derek Blank <derekblank@Dereks-MacBook-Pro.local> --- .../components/media-upload/index.native.js | 8 ++- .../media-upload/test/index.native.js | 37 ++++++++-- .../block-library/src/image/edit.native.js | 68 ++++++++++++++++++- .../src/image/styles.native.scss | 11 +++ 4 files changed, 113 insertions(+), 11 deletions(-) diff --git a/packages/block-editor/src/components/media-upload/index.native.js b/packages/block-editor/src/components/media-upload/index.native.js index 05aabd9a9a3cd1..8f793e4c0fa3f6 100644 --- a/packages/block-editor/src/components/media-upload/index.native.js +++ b/packages/block-editor/src/components/media-upload/index.native.js @@ -39,6 +39,7 @@ export const OPTION_TAKE_VIDEO = __( 'Take a Video' ); export const OPTION_TAKE_PHOTO = __( 'Take a Photo' ); export const OPTION_TAKE_PHOTO_OR_VIDEO = __( 'Take a Photo or Video' ); export const OPTION_INSERT_FROM_URL = __( 'Insert from URL' ); +export const OPTION_WORDPRESS_MEDIA_LIBRARY = __( 'WordPress Media Library' ); const URL_MEDIA_SOURCE = 'URL'; @@ -78,6 +79,8 @@ export class MediaUpload extends Component { } getAllSources() { + const { onSelectURL } = this.props; + const cameraImageSource = { id: mediaSources.deviceCamera, // ID is the value sent to native. value: mediaSources.deviceCamera + '-IMAGE', // This is needed to diferenciate image-camera from video-camera sources. @@ -124,16 +127,17 @@ export class MediaUpload extends Component { id: URL_MEDIA_SOURCE, value: URL_MEDIA_SOURCE, label: __( 'Insert from URL' ), - types: [ MEDIA_TYPE_AUDIO ], + types: [ MEDIA_TYPE_AUDIO, MEDIA_TYPE_IMAGE ], icon: globe, }; + // Only include `urlSource` option if `onSelectURL` prop is present, in order to match the web behavior. const internalSources = [ deviceLibrarySource, cameraImageSource, cameraVideoSource, siteLibrarySource, - urlSource, + ...( onSelectURL ? [ urlSource ] : [] ), ]; return internalSources.concat( this.state.otherMediaOptions ); diff --git a/packages/block-editor/src/components/media-upload/test/index.native.js b/packages/block-editor/src/components/media-upload/test/index.native.js index e54a9f5219321d..73cbf41b7dfc14 100644 --- a/packages/block-editor/src/components/media-upload/test/index.native.js +++ b/packages/block-editor/src/components/media-upload/test/index.native.js @@ -20,6 +20,7 @@ import { OPTION_TAKE_VIDEO, OPTION_TAKE_PHOTO, OPTION_INSERT_FROM_URL, + OPTION_WORDPRESS_MEDIA_LIBRARY, } from '../index'; const MEDIA_URL = 'http://host.media.type'; @@ -33,8 +34,11 @@ describe( 'MediaUpload component', () => { expect( wrapper ).toBeTruthy(); } ); - it( 'shows right media capture option for media type', () => { - const expectOptionForMediaType = ( mediaType, expectedOption ) => { + describe( 'Media capture options for different media block types', () => { + const expectOptionForMediaType = async ( + mediaType, + expectedOptions + ) => { const wrapper = render( <MediaUpload allowedTypes={ [ mediaType ] } @@ -52,11 +56,32 @@ describe( 'MediaUpload component', () => { ); fireEvent.press( wrapper.getByText( 'Open Picker' ) ); - wrapper.getByText( expectedOption ); + expectedOptions.forEach( ( item ) => { + const option = wrapper.getByText( item ); + expect( option ).toBeVisible(); + } ); }; - expectOptionForMediaType( MEDIA_TYPE_IMAGE, OPTION_TAKE_PHOTO ); - expectOptionForMediaType( MEDIA_TYPE_VIDEO, OPTION_TAKE_VIDEO ); - expectOptionForMediaType( MEDIA_TYPE_AUDIO, OPTION_INSERT_FROM_URL ); + + it( 'shows the correct media capture options for the Image block', () => { + expectOptionForMediaType( MEDIA_TYPE_IMAGE, [ + OPTION_TAKE_PHOTO, + OPTION_WORDPRESS_MEDIA_LIBRARY, + OPTION_INSERT_FROM_URL, + ] ); + } ); + + it( 'shows the correct media capture options for the Video block', () => { + expectOptionForMediaType( MEDIA_TYPE_VIDEO, [ + OPTION_TAKE_VIDEO, + OPTION_WORDPRESS_MEDIA_LIBRARY, + ] ); + } ); + + it( 'shows the correct media capture options for the Audio block', () => { + expectOptionForMediaType( MEDIA_TYPE_AUDIO, [ + OPTION_INSERT_FROM_URL, + ] ); + } ); } ); const expectMediaPickerForOption = ( diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index f9025bf2333f9c..24df9fce2866a4 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -1,7 +1,12 @@ /** * External dependencies */ -import { View, TouchableWithoutFeedback } from 'react-native'; +import { + ActivityIndicator, + Image as RNImage, + TouchableWithoutFeedback, + View, +} from 'react-native'; import { useRoute } from '@react-navigation/native'; /** @@ -45,7 +50,7 @@ import { blockSettingsScreens, } from '@wordpress/block-editor'; import { __, _x, sprintf } from '@wordpress/i18n'; -import { getProtocol, hasQueryArg } from '@wordpress/url'; +import { getProtocol, hasQueryArg, isURL } from '@wordpress/url'; import { doAction, hasAction } from '@wordpress/hooks'; import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; @@ -57,6 +62,7 @@ import { } from '@wordpress/icons'; import { store as coreStore } from '@wordpress/core-data'; import { store as editPostStore } from '@wordpress/edit-post'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -207,6 +213,7 @@ export class ImageEdit extends Component { this.onImagePressed = this.onImagePressed.bind( this ); this.onSetFeatured = this.onSetFeatured.bind( this ); this.onFocusCaption = this.onFocusCaption.bind( this ); + this.onSelectURL = this.onSelectURL.bind( this ); this.updateAlignment = this.updateAlignment.bind( this ); this.accessibilityLabelCreator = this.accessibilityLabelCreator.bind( this @@ -461,6 +468,45 @@ export class ImageEdit extends Component { } ); } + onSelectURL( newURL ) { + const { + createErrorNotice, + imageDefaultSize, + setAttributes, + } = this.props; + + if ( isURL( newURL ) ) { + this.setState( { + isFetchingImage: true, + } ); + + // Use RN's Image.getSize to determine if URL is a valid image + RNImage.getSize( + newURL, + () => { + setAttributes( { + url: newURL, + id: undefined, + width: undefined, + height: undefined, + sizeSlug: imageDefaultSize, + } ); + this.setState( { + isFetchingImage: false, + } ); + }, + () => { + createErrorNotice( __( 'Image file not found.' ) ); + this.setState( { + isFetchingImage: false, + } ); + } + ); + } else { + createErrorNotice( __( 'Invalid URL.' ) ); + } + } + onFocusCaption() { if ( this.props.onFocus ) { this.props.onFocus(); @@ -484,6 +530,14 @@ export class ImageEdit extends Component { ); } + showLoadingIndicator() { + return ( + <View style={ styles.image__loading }> + <ActivityIndicator animating /> + </View> + ); + } + getWidth() { const { attributes } = this.props; const { align, width } = attributes; @@ -611,7 +665,7 @@ export class ImageEdit extends Component { } render() { - const { isCaptionSelected } = this.state; + const { isCaptionSelected, isFetchingImage } = this.state; const { attributes, isSelected, @@ -713,9 +767,11 @@ export class ImageEdit extends Component { if ( ! url ) { return ( <View style={ styles.content }> + { isFetchingImage && this.showLoadingIndicator() } <MediaPlaceholder allowedTypes={ [ MEDIA_TYPE_IMAGE ] } onSelect={ this.onSelectMediaUploadOption } + onSelectURL={ this.onSelectURL } icon={ this.getPlaceholderIcon() } onFocus={ this.props.onFocus } autoOpenMediaUpload={ @@ -784,6 +840,8 @@ export class ImageEdit extends Component { } ) => { return ( <View style={ imageContainerStyles }> + { isFetchingImage && + this.showLoadingIndicator() } <Image align={ align && alignToFlex[ align ] @@ -836,6 +894,7 @@ export class ImageEdit extends Component { allowedTypes={ [ MEDIA_TYPE_IMAGE ] } isReplacingMedia={ true } onSelect={ this.onSelectMediaUploadOption } + onSelectURL={ this.onSelectURL } render={ ( { open, getMediaOptions } ) => { return getImageComponent( open, getMediaOptions ); } } @@ -881,7 +940,10 @@ export default compose( [ }; } ), withDispatch( ( dispatch ) => { + const { createErrorNotice } = dispatch( noticesStore ); + return { + createErrorNotice, closeSettingsBottomSheet() { dispatch( editPostStore ).closeGeneralSidebar(); }, diff --git a/packages/block-library/src/image/styles.native.scss b/packages/block-library/src/image/styles.native.scss index 476bb8b2be4716..0bcec21b571893 100644 --- a/packages/block-library/src/image/styles.native.scss +++ b/packages/block-library/src/image/styles.native.scss @@ -57,3 +57,14 @@ .removeFeaturedButton { color: $alert-red; } + +.image__loading { + align-items: center; + background-color: rgba(10, 10, 10, 0.5); + flex: 1; + height: 100%; + justify-content: center; + position: absolute; + width: 100%; + z-index: 1; +} From a6dd4946db9283cdb2f4de2be2540cf4769ec32c Mon Sep 17 00:00:00 2001 From: Jos <jostnes@users.noreply.github.com> Date: Mon, 16 May 2022 10:33:48 +0800 Subject: [PATCH 16/32] [RNMobile] - E2E Simplify heading and lists blocks functions (#40670) * update tests using paragraph, heading and list blocks * fix slash inserter tests to work in ci * lint fixes * wait for ordered list to appear * lint fixes * extra click only on local env * wait to get backspace click reflected * re-add extra click only for local env * add wait to wait for backspace key to be reflected * lint fixes * break function, set position to get list block * lint fixes * use correct params, update function name * lint fixes * make maxIteration a parameter for isElementVisible * update xpath for list block * utilize waitForVisible for isElementVisible * lint fixes * add wait to getNumberOfParagraphBlocks and update xpath for android list block * update edit text xpath to be read from any level Co-authored-by: jos <17252150+jostnes@users.noreply.github.com> --- .../gutenberg-editor-heading-@canary.test.js | 29 ++-- .../gutenberg-editor-lists-@canary.test.js | 34 ++-- .../gutenberg-editor-lists-end.test.js | 21 +-- .../gutenberg-editor-lists.test.js | 31 ++-- .../gutenberg-editor-paragraph.test.js | 20 +-- .../gutenberg-editor-paste.test.js | 14 +- .../gutenberg-editor-rotation.test.js | 19 +-- ...berg-editor-slash-inserter-@canary.test.js | 78 +++------ .../__device-tests__/helpers/utils.js | 94 ++++++++--- .../__device-tests__/pages/editor-page.js | 157 +++++++++--------- 10 files changed, 233 insertions(+), 264 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js index 17f58bfec5f71e..f806b43d2f8e42 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-heading-@canary.test.js @@ -2,26 +2,18 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { isAndroid } from './helpers/utils'; import testData from './helpers/test-data'; describe( 'Gutenberg Editor tests', () => { it( 'should be able to create a post with heading and paragraph blocks', async () => { await editorPage.addNewBlock( blockNames.heading ); - let headingBlockElement = await editorPage.getBlockAtPosition( - blockNames.heading, - 1, - { - useWaitForVisible: true, - } + let headingBlockElement = await editorPage.getTextBlockAtPosition( + blockNames.heading ); - if ( isAndroid() ) { - await headingBlockElement.click(); - } - await editorPage.sendTextToHeadingBlock( + + await editorPage.typeTextToTextBlock( headingBlockElement, - testData.heading, - false + testData.heading ); await editorPage.addNewBlock( blockNames.paragraph ); @@ -29,7 +21,7 @@ describe( 'Gutenberg Editor tests', () => { blockNames.paragraph, 2 ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.mediumText ); @@ -39,7 +31,7 @@ describe( 'Gutenberg Editor tests', () => { blockNames.paragraph, 3 ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.mediumText ); @@ -49,7 +41,7 @@ describe( 'Gutenberg Editor tests', () => { blockNames.heading, 4 ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( headingBlockElement, testData.heading ); @@ -59,9 +51,12 @@ describe( 'Gutenberg Editor tests', () => { blockNames.paragraph, 5 ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.mediumText ); + + // Assert that even though there are 5 blocks, there should only be 3 paragraph blocks + expect( await editorPage.getNumberOfParagraphBlocks() ).toEqual( 3 ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js index 80939bd9e58018..970fd669675283 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-@canary.test.js @@ -8,27 +8,26 @@ import testData from './helpers/test-data'; describe( 'Gutenberg Editor tests for List block', () => { it( 'should be able to add a new List block', async () => { await editorPage.addNewBlock( blockNames.list ); - const listBlockElement = await editorPage.getBlockAtPosition( - blockNames.list - ); - // Click List block on Android to force EditText focus - if ( isAndroid() ) { - await listBlockElement.click(); - } + let listBlockElement = await editorPage.getListBlockAtPosition( 1, { + isEmptyBlock: true, + } ); - // Send the first list item text. - await editorPage.sendTextToListBlock( + await editorPage.typeTextToTextBlock( listBlockElement, - testData.listItem1 + testData.listItem1, + false ); + listBlockElement = await editorPage.getListBlockAtPosition(); + // Send an Enter. - await editorPage.sendTextToListBlock( listBlockElement, '\n' ); + await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); // Send the second list item text. - await editorPage.sendTextToListBlock( + await editorPage.typeTextToTextBlock( listBlockElement, - testData.listItem2 + testData.listItem2, + false ); // Switch to html and verify html. @@ -38,12 +37,11 @@ describe( 'Gutenberg Editor tests for List block', () => { // This test depends on being run immediately after 'should be able to add a new List block' it( 'should update format to ordered list, using toolbar button', async () => { - let listBlockElement = await editorPage.getBlockAtPosition( - blockNames.list - ); + let listBlockElement = await editorPage.getListBlockAtPosition(); - // Click List block to force EditText focus. - await listBlockElement.click(); + if ( isAndroid() ) { + await listBlockElement.click(); + } // Send a click on the order list format button. await editorPage.clickOrderedListToolBarButton(); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js index d113be5371a387..e242f2f6f99e08 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists-end.test.js @@ -2,32 +2,21 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { isAndroid } from './helpers/utils'; import testData from './helpers/test-data'; describe( 'Gutenberg Editor tests for List block (end)', () => { it( 'should be able to end a List block', async () => { await editorPage.addNewBlock( blockNames.list ); - const listBlockElement = await editorPage.getBlockAtPosition( - blockNames.list - ); - - // Click List block on Android to force EditText focus - if ( isAndroid() ) { - await listBlockElement.click(); - } + const listBlockElement = await editorPage.getListBlockAtPosition(); - // Send the first list item text. - await editorPage.sendTextToListBlock( + await editorPage.typeTextToTextBlock( listBlockElement, - testData.listItem1 + testData.listItem1, + false ); // Send an Enter. - await editorPage.sendTextToListBlock( listBlockElement, '\n' ); - - // Send an Enter. - await editorPage.sendTextToListBlock( listBlockElement, '\n' ); + await editorPage.typeTextToTextBlock( listBlockElement, '\n\n', false ); const html = await editorPage.getHtmlContent(); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js index 12537bfcae5350..6b2cb1c04509ff 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-lists.test.js @@ -2,28 +2,33 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { backspace, isAndroid } from './helpers/utils'; +import { waitIfAndroid, backspace } from './helpers/utils'; describe( 'Gutenberg Editor tests for List block', () => { // Prevent regression of https://github.com/wordpress-mobile/gutenberg-mobile/issues/871 it( 'should handle spaces in a list', async () => { await editorPage.addNewBlock( blockNames.list ); - let listBlockElement = await editorPage.getBlockAtPosition( - blockNames.list - ); - // Click List block on Android to force EditText focus - if ( isAndroid() ) { - await listBlockElement.click(); - } + let listBlockElement = await editorPage.getListBlockAtPosition(); // Send the list item text. - await editorPage.sendTextToListBlock( listBlockElement, ' a' ); + await editorPage.typeTextToTextBlock( listBlockElement, ' a', false ); // Send an Enter. - await editorPage.sendTextToListBlock( listBlockElement, '\n' ); + await editorPage.typeTextToTextBlock( listBlockElement, '\n', false ); + + // Instead of introducing separate conditions for local and CI environment, add this wait for Android to accomodate both environments + await waitIfAndroid(); // Send a backspace. - await editorPage.sendTextToListBlock( listBlockElement, backspace ); + await editorPage.typeTextToTextBlock( + listBlockElement, + backspace, + false + ); + + // There is a delay in Sauce Labs when a key is sent + // There isn't an element to check as it's being typed into an element that already exists, workaround is to add this wait until there's a better solution + await waitIfAndroid(); // Switch to html and verify html. const html = await editorPage.getHtmlContent(); @@ -35,9 +40,7 @@ describe( 'Gutenberg Editor tests for List block', () => { ); // Remove list block to reset editor to clean state. - listBlockElement = await editorPage.getBlockAtPosition( - blockNames.list - ); + listBlockElement = await editorPage.getListBlockAtPosition(); await listBlockElement.click(); await editorPage.removeBlockAtPosition( blockNames.list ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js index 0ac9bae0743ef3..20d7d690b96bfc 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-paragraph.test.js @@ -16,12 +16,12 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.shortText ); await clickMiddleOfElement( editorPage.driver, paragraphBlockElement ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, '\n', false @@ -44,19 +44,13 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { let paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.shortText ); await clickMiddleOfElement( editorPage.driver, paragraphBlockElement ); - await editorPage.typeTextToParagraphBlock( - paragraphBlockElement, - '\n' - ); + await editorPage.typeTextToTextBlock( paragraphBlockElement, '\n' ); const text0 = await editorPage.getTextForParagraphBlockAtPosition( 1 ); const text1 = await editorPage.getTextForParagraphBlockAtPosition( 2 ); @@ -71,7 +65,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { paragraphBlockElement ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, backspace ); @@ -112,7 +106,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { editorPage.driver, paragraphBlockElement ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, backspace ); @@ -140,7 +134,7 @@ describe( 'Gutenberg Editor tests for Paragraph Block', () => { blockNames.paragraph, 2 ); - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, backspace ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js index 16c291584b8ed7..7404b0969d6adb 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-paste.test.js @@ -29,11 +29,8 @@ describe( 'Gutenberg Editor paste tests', () => { const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.pastePlainText ); @@ -59,9 +56,6 @@ describe( 'Gutenberg Editor paste tests', () => { blockNames.paragraph, 2 ); - if ( isAndroid() ) { - await paragraphBlockElement2.click(); - } // Paste into second paragraph block. await longPressMiddleOfElement( @@ -83,9 +77,6 @@ describe( 'Gutenberg Editor paste tests', () => { const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } // Copy content to clipboard. await longPressMiddleOfElement( @@ -108,9 +99,6 @@ describe( 'Gutenberg Editor paste tests', () => { blockNames.paragraph, 2 ); - if ( isAndroid() ) { - await paragraphBlockElement2.click(); - } // Paste into second paragraph block. await longPressMiddleOfElement( diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js index 9056854fa42c7f..215b81bf98b4d4 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-rotation.test.js @@ -11,11 +11,8 @@ describe( 'Gutenberg Editor tests', () => { let paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.mediumText ); @@ -23,27 +20,21 @@ describe( 'Gutenberg Editor tests', () => { await toggleOrientation( editorPage.driver ); // On Android the keyboard hides the add block button, let's hide it after rotation if ( isAndroid() ) { - await editorPage.driver.hideDeviceKeyboard(); + await editorPage.dismissKeyboard(); } await editorPage.addNewBlock( blockNames.paragraph ); if ( isAndroid() ) { - await editorPage.driver.hideDeviceKeyboard(); + await editorPage.dismissKeyboard(); } paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph, 2 ); - while ( ! paragraphBlockElement ) { - await editorPage.driver.hideDeviceKeyboard(); - paragraphBlockElement = await editorPage.getBlockAtPosition( - blockNames.paragraph, - 2 - ); - } - await editorPage.typeTextToParagraphBlock( + + await editorPage.typeTextToTextBlock( paragraphBlockElement, testData.mediumText ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js index 563445768709a7..d16e37bc7090f5 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-slash-inserter-@canary.test.js @@ -5,105 +5,68 @@ import { blockNames } from './pages/editor-page'; import { isAndroid } from './helpers/utils'; import { slashInserter, shortText } from './helpers/test-data'; -const ANIMATION_TIME = 200; - -// Helper function for asserting slash inserter presence. -async function assertSlashInserterPresent( checkIsVisible ) { - let areResultsDisplayed; - try { - const foundElements = await editorPage.driver.elementsByAccessibilityId( - 'Slash inserter results' - ); - areResultsDisplayed = !! foundElements.length; - } catch ( e ) { - areResultsDisplayed = false; - } - if ( checkIsVisible ) { - expect( areResultsDisplayed ).toBeTruthy(); - } else { - expect( areResultsDisplayed ).toBeFalsy(); - } -} - -// Due to flakiness, disabling until its more stable -// https://github.com/wordpress-mobile/gutenberg-mobile/issues/3699 -// eslint-disable-next-line jest/no-disabled-tests -describe.skip( 'Gutenberg Editor Slash Inserter tests', () => { +describe( 'Gutenberg Editor Slash Inserter tests', () => { it( 'should show the menu after typing /', async () => { await editorPage.addNewBlock( blockNames.paragraph ); - const paragraphBlockElement = await editorPage.getBlockAtPosition( + const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, slashInserter ); - await editorPage.driver.sleep( ANIMATION_TIME ); - - assertSlashInserterPresent( true ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( true ); await editorPage.removeBlockAtPosition( blockNames.paragraph ); } ); it( 'should hide the menu after deleting the / character', async () => { await editorPage.addNewBlock( blockNames.paragraph ); - const paragraphBlockElement = await editorPage.getBlockAtPosition( + const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, slashInserter ); - await editorPage.driver.sleep( ANIMATION_TIME ); - assertSlashInserterPresent( true ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( true ); // Remove / character. if ( isAndroid() ) { - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, `${ shortText }`, true ); } else { - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, `\b ${ shortText }`, false ); } - await editorPage.driver.sleep( ANIMATION_TIME ); // Check if the slash inserter UI no longer exists. - assertSlashInserterPresent( false ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( false ); await editorPage.removeBlockAtPosition( blockNames.paragraph ); } ); it( 'should add an Image block after tying /image and tapping on the Image block button', async () => { await editorPage.addNewBlock( blockNames.paragraph ); - const paragraphBlockElement = await editorPage.getBlockAtPosition( + const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, `${ slashInserter }image` ); - await editorPage.driver.sleep( ANIMATION_TIME ); - assertSlashInserterPresent( true ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( true ); // Find Image block button. const imageButtonElement = await editorPage.driver.elementByAccessibilityId( @@ -120,30 +83,27 @@ describe.skip( 'Gutenberg Editor Slash Inserter tests', () => { ).toBe( true ); // Slash inserter UI should not be present after adding a block. - assertSlashInserterPresent( false ); + expect( await editorPage.assertSlashInserterPresent() ).toBe( false ); // Remove image block. await editorPage.removeBlockAtPosition( blockNames.image ); } ); - it( 'should insert an image block with "/img" + enter', async () => { + it( 'should insert an embed image block with "/img" + enter', async () => { await editorPage.addNewBlock( blockNames.paragraph ); - const paragraphBlockElement = await editorPage.getBlockAtPosition( + const paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); - if ( isAndroid() ) { - await paragraphBlockElement.click(); - } - await editorPage.typeTextToParagraphBlock( + await editorPage.typeTextToTextBlock( paragraphBlockElement, '/img\n', false ); expect( - await editorPage.hasBlockAtPosition( 1, blockNames.image ) + await editorPage.hasBlockAtPosition( 1, blockNames.embed ) ).toBe( true ); - await editorPage.removeBlockAtPosition( blockNames.image ); + await editorPage.removeBlockAtPosition( blockNames.embed ); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 03f3002c59b142..1658d553c9c7a2 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -464,48 +464,102 @@ const waitForMediaLibrary = async ( driver ) => { await waitForVisible( driver, locator ); }; -const waitForVisible = async ( driver, elementLocator, iteration = 0 ) => { - const maxIteration = 25; +/** + * @param {string} driver + * @param {string} elementLocator + * @param {number} maxIteration - Default value is 25 + * @param {number} iteration - Default value is 0 + * @return {string} - Returns the first element found, empty string if not found + */ +const waitForVisible = async ( + driver, + elementLocator, + maxIteration = 25, + iteration = 0 +) => { const timeout = 1000; if ( iteration >= maxIteration ) { - throw new Error( + // if element not found, print error and return empty string + // eslint-disable-next-line no-console + console.error( `"${ elementLocator }" is still not visible after ${ iteration } retries!` ); + return ''; } else if ( iteration !== 0 ) { // wait before trying to locate element again await driver.sleep( timeout ); } - const locator = await driver.elementsByXPath( elementLocator ); - if ( locator.length !== 1 ) { + const element = await driver.elementsByXPath( elementLocator ); + if ( element.length !== 1 ) { // if locator is not visible, try again - return waitForVisible( driver, elementLocator, iteration + 1 ); + return waitForVisible( + driver, + elementLocator, + maxIteration, + iteration + 1 + ); + } + + return element[ 0 ]; +}; + +/** + * @param {string} driver + * @param {string} elementLocator + * @param {number} maxIteration - Default value is 25, can be adjusted to be less to wait for element to not be visible + * @return {boolean} - Returns true if element is found, false otherwise + */ +const isElementVisible = async ( + driver, + elementLocator, + maxIteration = 25 +) => { + const element = await waitForVisible( + driver, + elementLocator, + maxIteration + ); + + // if there is no element, return false + if ( ! element ) { + return false; + } + + return true; +}; + +// Only for Android +const waitIfAndroid = async () => { + if ( isAndroid() ) { + await editorPage.driver.sleep( 1000 ); } - return locator[ 0 ]; }; module.exports = { backspace, - timer, - setupDriver, - isLocalEnvironment, - isAndroid, - typeString, - clickMiddleOfElement, clickBeginningOfElement, + clickMiddleOfElement, + doubleTap, + isAndroid, + isEditorVisible, + isElementVisible, + isLocalEnvironment, longPressMiddleOfElement, - tapSelectAllAboveElement, - tapCopyAboveElement, - tapPasteAboveElement, + setupDriver, + stopDriver, swipeDown, - swipeUp, swipeFromTo, - stopDriver, + swipeUp, + tapCopyAboveElement, + tapPasteAboveElement, + tapSelectAllAboveElement, + timer, toggleHtmlMode, toggleOrientation, - doubleTap, - isEditorVisible, + typeString, waitForMediaLibrary, waitForVisible, + waitIfAndroid, }; diff --git a/packages/react-native-editor/__device-tests__/pages/editor-page.js b/packages/react-native-editor/__device-tests__/pages/editor-page.js index f408d8baf0fc8f..440e256566ef91 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -2,17 +2,18 @@ * Internal dependencies */ const { + doubleTap, + isAndroid, + isEditorVisible, + isElementVisible, + longPressMiddleOfElement, setupDriver, stopDriver, - isAndroid, - swipeUp, swipeDown, - typeString, - toggleHtmlMode, swipeFromTo, - longPressMiddleOfElement, - doubleTap, - isEditorVisible, + swipeUp, + toggleHtmlMode, + typeString, waitForVisible, } = require( '../helpers/utils' ); @@ -45,11 +46,15 @@ class EditorPage { return await this.driver.hasElementByAccessibilityId( 'block-list' ); } - // For text blocks, e.g. Paragraph, Heading + // =============================== + // Text blocks functions + // E.g. Paragraph, Heading blocks + // =============================== async getTextBlockAtPosition( blockName, position = 1 ) { - // iOS needs a click before + // iOS needs a click to get the text element if ( ! isAndroid() ) { const textBlockLocator = `(//XCUIElementTypeButton[contains(@name, "${ blockName } Block. Row ${ position }")])`; + const textBlock = await waitForVisible( this.driver, textBlockLocator @@ -64,6 +69,10 @@ class EditorPage { return await waitForVisible( this.driver, blockLocator ); } + async typeTextToTextBlock( block, text, clear ) { + await typeString( this.driver, block, text, clear ); + } + // Finds the wd element for new block that was added and sets the element attribute // and accessibilityId attributes on this object and selects the block // position uses one based numbering. @@ -506,10 +515,6 @@ class EditorPage { // Paragraph Block functions // ========================= - async typeTextToParagraphBlock( block, text, clear ) { - await typeString( this.driver, block, text, clear ); - } - async sendTextToParagraphBlock( position, text, clear ) { const paragraphs = text.split( '\n' ); for ( let i = 0; i < paragraphs.length; i++ ) { @@ -522,13 +527,9 @@ class EditorPage { await block.click(); } - await this.typeTextToParagraphBlock( - block, - paragraphs[ i ], - clear - ); + await this.typeTextToTextBlock( block, paragraphs[ i ], clear ); if ( i !== paragraphs.length - 1 ) { - await this.typeTextToParagraphBlock( block, '\n', false ); + await this.typeTextToTextBlock( block, '\n', false ); } } } @@ -542,35 +543,68 @@ class EditorPage { return await blockLocator.text(); } + async getNumberOfParagraphBlocks() { + const paragraphBlockLocator = isAndroid() + ? `//android.view.ViewGroup[contains(@content-desc, "Paragraph Block. Row")]//android.widget.EditText` + : `(//XCUIElementTypeButton[contains(@name, "Paragraph Block. Row")])`; + + const locator = await this.driver.elementsByXPath( + paragraphBlockLocator + ); + return locator.length; + } + + async assertSlashInserterPresent() { + const slashInserterLocator = isAndroid() + ? '//android.widget.HorizontalScrollView[@content-desc="Slash inserter results"]/android.view.ViewGroup' + : '(//XCUIElementTypeOther[@name="Slash inserter results"])[1]'; + + return await isElementVisible( this.driver, slashInserterLocator, 5 ); + } + // ========================= // List Block functions // ========================= - async getTextViewForListBlock( block ) { - let textViewElementName = 'XCUIElementTypeTextView'; - if ( isAndroid() ) { - textViewElementName = 'android.widget.EditText'; - } + async getListBlockAtPosition( + position = 1, + options = { isEmptyBlock: false } + ) { + // iOS needs a few extra steps to get the text element + if ( ! isAndroid() ) { + // Wait for and click the list in the correct position + let listBlock = await waitForVisible( + this.driver, + `(//XCUIElementTypeOther[contains(@name, "List Block. Row ${ position }")])[1]` + ); + await listBlock.click(); - const accessibilityId = await block.getAttribute( - this.accessibilityIdKey - ); - const blockLocator = `//*[@${ - this.accessibilityIdXPathAttrib - }=${ JSON.stringify( accessibilityId ) }]//${ textViewElementName }`; - return await this.driver.elementByXPath( blockLocator ); - } + const listBlockLocator = options.isEmptyBlock + ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` + : `//XCUIElementTypeButton[contains(@name, "List")]`; - async sendTextToListBlock( block, text ) { - const textViewElement = await this.getTextViewForListBlock( block ); + // Wait for and click the list to get the text element + listBlock = await waitForVisible( this.driver, listBlockLocator ); + await listBlock.click(); + } - // Cannot clear list blocks because it messes up the list bullet. - const clear = false; + const listBlockTextLocatorIOS = options.isEmptyBlock + ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` + : `//XCUIElementTypeButton[contains(@name, "List")]//XCUIElementTypeTextView`; - return await typeString( this.driver, textViewElement, text, clear ); + const listBlockTextLocator = isAndroid() + ? `//android.view.ViewGroup[contains(@content-desc, "List Block. Row ${ position }")]//android.widget.EditText` + : listBlockTextLocatorIOS; + + return await waitForVisible( this.driver, listBlockTextLocator ); } async clickOrderedListToolBarButton() { + const toolBarLocator = isAndroid() + ? `//android.widget.Button[@content-desc="${ this.orderedListButtonName }"]` + : `//XCUIElementTypeButton[@name="${ this.orderedListButtonName }"]`; + + await waitForVisible( this.driver, toolBarLocator ); await this.clickToolBarButton( this.orderedListButtonName ); } @@ -609,34 +643,6 @@ class EditorPage { await typeString( this.driver, imageBlockCaptionField, caption, clear ); } - // ========================= - // Heading Block functions - // ========================= - - // Inner element changes on iOS if Heading Block is empty - async getTextViewForHeadingBlock( block, empty ) { - let textViewElementName = empty - ? 'XCUIElementTypeStaticText' - : 'XCUIElementTypeTextView'; - if ( isAndroid() ) { - textViewElementName = 'android.widget.EditText'; - } - - const accessibilityId = await block.getAttribute( - this.accessibilityIdKey - ); - const blockLocator = `//*[@${ this.accessibilityIdXPathAttrib }="${ accessibilityId }"]//${ textViewElementName }`; - return await this.driver.elementByXPath( blockLocator ); - } - - async sendTextToHeadingBlock( block, text, clear = true ) { - const textViewElement = await this.getTextViewForHeadingBlock( - block, - true - ); - return await typeString( this.driver, textViewElement, text, clear ); - } - async closePicker() { if ( isAndroid() ) { // Wait for media block picker to load before closing @@ -760,34 +766,25 @@ class EditorPage { async sauceJobStatus( allPassed ) { await this.driver.sauceJobStatus( allPassed ); } - - async getNumberOfParagraphBlocks() { - const paragraphBlockLocator = isAndroid() - ? `//android.view.ViewGroup[contains(@content-desc, "Paragraph Block. Row")]//android.widget.EditText` - : `(//XCUIElementTypeButton[contains(@name, "Paragraph Block. Row")])`; - const locator = await this.driver.elementsByXPath( - paragraphBlockLocator - ); - return locator.length; - } } const blockNames = { - paragraph: 'Paragraph', - gallery: 'Gallery', + audio: 'Audio', columns: 'Columns', cover: 'Cover', + embed: 'Embed', + file: 'File', + gallery: 'Gallery', heading: 'Heading', image: 'Image', latestPosts: 'Latest Posts', list: 'List', more: 'More', + paragraph: 'Paragraph', + search: 'Search', separator: 'Separator', spacer: 'Spacer', verse: 'Verse', - file: 'File', - audio: 'Audio', - search: 'Search', }; module.exports = { initializeEditorPage, blockNames }; From 1b3391e790cd76310c9deb43e804201cf9f83542 Mon Sep 17 00:00:00 2001 From: Jason Johnston <jhnstn@users.noreply.github.com> Date: Thu, 12 May 2022 05:58:43 -0400 Subject: [PATCH 17/32] Add ruby version file (#41013) Use same version of ruby that is used on WP iOS --- packages/react-native-editor/ios/.ruby-version | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/react-native-editor/ios/.ruby-version diff --git a/packages/react-native-editor/ios/.ruby-version b/packages/react-native-editor/ios/.ruby-version new file mode 100644 index 00000000000000..a4dd9dba4fbfc5 --- /dev/null +++ b/packages/react-native-editor/ios/.ruby-version @@ -0,0 +1 @@ +2.7.4 From 148f835cc157124afe655cb52b9232eacbdc403a Mon Sep 17 00:00:00 2001 From: Siobhan Bamber <siobhan@automattic.com> Date: Mon, 23 May 2022 13:15:00 +0100 Subject: [PATCH 18/32] [RNMobile] Improvements to Getting Started Guides (#40964) * Update with copy from Gutenberg Mobile Gutenberg Mobile's readme had some copy in the troubleshooting section that wasn't included in Gutenberg's how-to: https://github.com/wordpress-mobile/gutenberg-mobile/edit/trunk/README.md This commit fixes that issue by updating Gutenberg's how-to with the latest from Gutenberg Mobile. * Tweak wording, update inconsistent capitalisation * Tweaks to copy and formatting With this commit, I've tidied up some of the copy, attempting to make the instructions as concise and helpful as possible. I've also set the images to the same width to tidy up the overall look of the document (the images will appear small on mobile devices, but can be tapped on to get the full image). * Set all images to fixed width, for consistency * Rearrange sections to improve overall flow - The "Unit Tests" is moved towards the bottom of the document, so that's it's closer to the instructions for integration tests. the document flows as follows: - The steps for installing Xcode are moved under the "iOS" section. - The overall flow of the document has been updated to the following: Clone Gutenberg > iOS > Android > Tests * Add details about recommended JDK * Add details about Cocoapods * Refine instructions for installing Cocoapods * Note `ffi` may already be installed Some versions of Ruby may not require the `ffi` to be manually installed, which is noted with this commit. --- .../getting-started-react-native.md | 2 + .../code/react-native/osx-setup-guide.md | 166 ++++++++++-------- 2 files changed, 90 insertions(+), 78 deletions(-) diff --git a/docs/contributors/code/react-native/getting-started-react-native.md b/docs/contributors/code/react-native/getting-started-react-native.md index 95a14e7eb50f5c..72cad307673861 100644 --- a/docs/contributors/code/react-native/getting-started-react-native.md +++ b/docs/contributors/code/react-native/getting-started-react-native.md @@ -72,6 +72,8 @@ To see a list of all of your available iOS devices, use `xcrun simctl list devic ### Troubleshooting +If the Android emulator doesn't start correctly, or compiling fails with `Could not initialize class org.codehaus.groovy.runtime.InvokerHelper` or similar, it may help to double check the set up of your development environment against the latest requirements in [React Native's documentation](https://reactnative.dev/docs/environment-setup). With Android Studio, for example, you will need to configure the `ANDROID_HOME` environment variable and ensure that your version of JDK matches the latest requirements. + Some times, and especially when tweaking anything in the `package.json`, Babel configuration (`.babelrc`) or the Jest configuration (`jest.config.js`), your changes might seem to not take effect as expected. On those times, you might need to stop the metro bunder process and restart it with `npm run native start:reset`. Other times, you might want to reinstall the NPM packages from scratch and the `npm run native clean:install` script can be handy. ## Developing with Visual Studio Code diff --git a/docs/contributors/code/react-native/osx-setup-guide.md b/docs/contributors/code/react-native/osx-setup-guide.md index 3d68364295f5dd..4f1d160a4b2b61 100644 --- a/docs/contributors/code/react-native/osx-setup-guide.md +++ b/docs/contributors/code/react-native/osx-setup-guide.md @@ -1,47 +1,22 @@ # Setup guide for React Native development (macOS) -Are you interested in contributing to the native mobile editor? This -guide is a detailed walk through designed to get you up and running! +Are you interested in contributing to the native mobile editor? This guide is a detailed walk through designed to get you up and running! -Note that the following instructions here are primarily focused on the -macOS environment. For other environments, [the React Native quickstart documentation](https://reactnative.dev/docs/environment-setup) -has helpful pointers and steps for getting set up. - -## Install Xcode - -Install [Xcode](https://developer.apple.com/xcode/) via the app store. We'll be using -the XCode to both compile the iOS app and use the phone simulator app. - -Once it has been installed from the App Store, open it by visiting `Applications > Xcode` - -After opening the application: - -- Accept the license agreement. -- Verify that `Xcode > Preferences > Locations > Command Line Tools` points at the current Xcode version. - -<img src="https://developer.wordpress.org/files/2021/10/xcode-command-line-tools.png" width="500" alt="Screenshot of XCode command line tools settings."> +Note that these instructions are primarily focused on the macOS environment. For other environments, [the React Native quickstart documentation](https://reactnative.dev/docs/environment-setup) has helpful pointers and steps for getting set up. ## Clone Gutenberg -If Xcode was installed successfully, we'll also have a version of git available. (It's possible to -update this later if we want to use a more recent version). - -In a terminal run: - ```sh git clone git@github.com:WordPress/gutenberg.git ``` ### Install node and npm -If you’re working in multiple JS projects, a node version manager may make sense. A manager will let you -switch between different node and npm versions of your choosing. - -Some good options are [nvm](https://github.com/nvm-sh/nvm) or [volta](https://volta.sh/). +If you’re working in multiple JS projects, a node version manager may make sense. A manager will let you switch between different node and npm versions of your choosing. -Pick one and follow the install instructions noted on their website. +We recommend [nvm](https://github.com/nvm-sh/nvm). -Then run: +After installing nvm, run the following from the top-level directory of the cloned project: ```sh nvm install 'lts/*' @@ -49,19 +24,13 @@ nvm alias default 'lts/*' # sets this as the default when opening a new terminal nvm use # switches to the project settings ``` -Or - -```sh -volta install node #defaults to installing lts -``` - -Then install dependencies from your Gutenberg checkout folder: +Then install dependencies: ``` npm ci ``` -#### Do you have an older existing Gutenberg checkout? +### Do you have an older existing Gutenberg checkout? If you have an existing Gutenberg checkout be sure to fully clean `node_modules` and re-install dependencies. This may help avoid errors in the future. @@ -71,28 +40,56 @@ npm run distclean npm ci ``` -## Unit Tests +## iOS -Unit tests should work at this point. +### CocoaPods -```sh -npm run native test +[CocoaPods](https://guides.cocoapods.org/using/getting-started.html) is required to fetch React and third-party dependencies. The steps to install it vary depending on how Ruby is managed on your machine. + +#### System Ruby + +If you're using the default Ruby available with MacOS, you'll need to use the `sudo` command to install gems like Cocoapods: + +``` +sudo gem install cocoapods ``` -## iOS +Note, Mac M1 is not directly compatible with Cocoapods. If you encounter issues, try running these commands to install the ffi package, which will enable pods to be installed with the proper architecture: + +``` +sudo arch -x86_64 gem install ffi +arch -x86_64 pod install +``` + +#### Ruby Manager + +It may not be necessary to manually install Cocoapods or the `ffi` package if you're using a Ruby Version manager. Please refer to your chosen manager's documentation for guidance. + +[`rbenv`](https://github.com/rbenv/rbenv) is the recommended manager if you're running Gutenberg from within [the WordPress iOS app](https://github.com/wordpress-mobile/WordPress-iOS) (vs. only the demo app). -The easiest way to figure out what needs to be installed is using the -[react native doctor](https://reactnative.dev/blog/2019/11/18/react-native-doctor). From your checkout, or -relative to `/packages/react-native-editor folder`, run: +### Set up Xcode + +Install [Xcode](https://developer.apple.com/xcode/) via the app store and then open it up: + +- Accept the license agreement. +- Verify that `Xcode > Preferences > Locations > Command Line Tools` points to the current Xcode version. + +<img src="https://developer.wordpress.org/files/2021/10/xcode-command-line-tools.png" width="700px" alt="Screenshot of XCode command line tools settings."> + +### react-native doctor + +[react-native doctor](https://reactnative.dev/blog/2019/11/18/react-native-doctor) can be used to identify anything that's missing from your development environment. From your Gutenberg checkout, or relative to `/packages/react-native-editor folder`, run: ```sh npx @react-native-community/cli doctor ``` -<img src="https://developer.wordpress.org/files/2021/10/react-native-doctor.png" width="500px" alt="Screenshot of react-native-community/cli doctor tool running in the terminal."> +<img src="https://developer.wordpress.org/files/2021/10/react-native-doctor.png" width="700px" alt="Screenshot of react-native-community/cli doctor tool running in the terminal."> See if `doctor` can fix both "common" and "iOS" issues. (Don't worry if "Android" still has ❌s at this stage, we'll get to those later!) +### Run the demo app + Once all common and iOS issues are resolved, try: ``` @@ -105,37 +102,46 @@ In another terminal type: npm run native ios ``` -After waiting for everything to build we should see: +After waiting for everything to build, the demo app should be running from the iOS simulator: -<img src="https://developer.wordpress.org/files/2021/10/iOS-Simulator.png" alt="Screenshot of the block editor in iOS simulator." /> +<img src="https://developer.wordpress.org/files/2021/10/iOS-Simulator.png" width="700px" alt="Screenshot of the block editor in iOS simulator." /> ## Android -To keep things simple, let's use Android Studio for all JDK and SDK package management. -The first step is [downloading Android Studio](https://developer.android.com/studio). +### Java Development Kit (JDK) -Next, open an existing project and select the gutenberg folder you cloned: +The JDK recommended in [the React Native documentation](https://reactnative.dev/docs/environment-setup) is called Azul Zulu. It can be installed using [Homebrew](https://brew.sh/). To install it, run the following commands in a terminal after installing Homebrew: -Click on the cube with the down arrow: +``` +brew tap homebrew/cask-versions +brew install --cask zulu11 +``` -<img src="https://developer.wordpress.org/files/2021/10/react-native-package-manager.png" alt="Screenshot highlighting where the package manager button is located in Android Studio."> +If you already have a JDK installed on your system, it should be JDK 11 or newer. -We can download SDK platforms, packages and other tools on this screen. Specific versions are -hidden behind the "Show package details" checkbox, check it, since our build requires specific versions for E2E and -development: +### Set up Android Studio -<img src="https://developer.wordpress.org/files/2021/10/react-native-show-package-details.png" alt="Screenshot of the package manager in Android Studio, highlighting the Show Package Details checkbox."> +To compile the Android app, [download Android Studio](https://developer.android.com/studio). -Check all related packages from [build.gradle](https://github.com/WordPress/gutenberg/blob/trunk/packages/react-native-editor/android/build.gradle). -Then click on "Apply" to download items. There may be other related dependencies from build.gradle files in node_modules. -If you don’t want to dig through files, stack traces will complain of missing packages, but it does take quite a number -of tries if you go through this route. +Next, open an existing project and select the Gutenberg folder you cloned. -<img src="https://developer.wordpress.org/files/2021/10/react-native-editor-build-gradle.png" alt="Screenshot of the build.gradle configuration file."> +From here, click on the cube icon that's highlighted in the following screenshot to access the SDK Manager. Another way to the SDK Manager is to navigate to `Tools > SDK Manager`: -<img src="https://developer.wordpress.org/files/2021/10/react-native-sdk.png" width="500" alt="Screenshot of the package manager displaying SDK Platforms."> +<img src="https://developer.wordpress.org/files/2021/10/react-native-package-manager.png" width="700px" alt="Screenshot highlighting where the package manager button is located in Android Studio."> -<img src="https://developer.wordpress.org/files/2021/10/react-native-sdk-tools.png" width="500" alt="Screenshot of the package manager displaying SDK Tools."> +We can download SDK platforms, packages and other tools on this screen. Specific versions are hidden behind the "Show package details" checkbox, check it, since our build requires specific versions for E2E and development: + +<img src="https://developer.wordpress.org/files/2021/10/react-native-show-package-details.png" width="700px" alt="Screenshot of the package manager in Android Studio, highlighting the Show Package Details checkbox."> + +Check all related packages from [build.gradle](https://github.com/WordPress/gutenberg/blob/trunk/packages/react-native-editor/android/build.gradle). Then click on "Apply" to download items. There may be other related dependencies from build.gradle files in node_modules. + +If you don’t want to dig through files, stack traces will complain of missing packages, but it does take quite a number of tries if you go through this route. + +<img src="https://developer.wordpress.org/files/2021/10/react-native-editor-build-gradle.png" width="700px" alt="Screenshot of the build.gradle configuration file."> + +<img src="https://developer.wordpress.org/files/2021/10/react-native-sdk.png" width="700px" alt="Screenshot of the package manager displaying SDK Platforms."> + +<img src="https://developer.wordpress.org/files/2021/10/react-native-sdk-tools.png" width="700px" alt="Screenshot of the package manager displaying SDK Tools."> ### Update Paths @@ -167,26 +173,25 @@ source ~/.bash_profile If the SDK path can't be found, you can verify its location by visiting Android Studio > Preferences > System Settings > Android SDK -<img src="https://developer.wordpress.org/files/2021/10/sdk-path.png" alt="Screenshot of where the SDK Path may be found in Android Studio."> +<img src="https://developer.wordpress.org/files/2021/10/sdk-path.png" width="700px" alt="Screenshot of where the SDK Path may be found in Android Studio."> ### Create a new device image Next, let’s create a virtual device image. Click on the phone icon with an android to the bottom-right. -<img src="https://developer.wordpress.org/files/2021/10/react-native-android-device-manager-button.png" alt="Screenshot of where to find the android device manager button."> +<img src="https://developer.wordpress.org/files/2021/10/react-native-android-device-manager-button.png" width="700px" alt="Screenshot of where to find the android device manager button."> This brings up the “Android Virtual Device Manager” or (AVD). Click on “Create Virtual Device”. Pick a phone type of your choice: -<img src="https://developer.wordpress.org/files/2021/10/react-native-android-select-hardware.png" alt="Screenshot of the Virtual Device Configuration setup."> +<img src="https://developer.wordpress.org/files/2021/10/react-native-android-select-hardware.png" width="700px" alt="Screenshot of the Virtual Device Configuration setup."> -Pick the target SDK version. This is the targetSdkVersion set in the -[build.gradle](https://github.com/WordPress/gutenberg/blob/trunk/packages/react-native-editor/android/build.gradle) file. +Pick the target SDK version. This is the targetSdkVersion set in the [build.gradle](https://github.com/WordPress/gutenberg/blob/trunk/packages/react-native-editor/android/build.gradle) file. -<img src="https://developer.wordpress.org/files/2021/10/react-native-adv-system-image.png" alt="Screenshot of picking a system image in the Android Device Manager workflow."> +<img src="https://developer.wordpress.org/files/2021/10/react-native-adv-system-image.png" width="700px" alt="Screenshot of picking a system image in the Android Device Manager workflow."> There are some advanced settings we can toggle, but these are optional. Click finish. -### Putting it all together +### Run the demo app Start metro: @@ -202,7 +207,13 @@ npm run native android After a bit of a wait, we’ll see something like this: -<img src="https://developer.wordpress.org/files/2021/10/android-simulator.png" alt="Screenshot of a the block editor in Android Simulator."> +<img src="https://developer.wordpress.org/files/2021/10/android-simulator.png" width="700px" alt="Screenshot of a the block editor in Android Simulator."> + +## Unit Tests + +```sh +npm run native test +``` ## Integration Tests @@ -212,14 +223,13 @@ After a bit of a wait, we’ll see something like this: npx appium-doctor ``` -<img src="https://developer.wordpress.org/files/2021/10/CleanShot-2021-10-27-at-15.20.16.png" width="500px" alt="Screenshot of the appium-doctor tool running in the terminal."> +<img src="https://developer.wordpress.org/files/2021/10/CleanShot-2021-10-27-at-15.20.16.png" width="700px" alt="Screenshot of the appium-doctor tool running in the terminal."> Resolve any required dependencies. ### iOS Integration Tests -If we know we can run the iOS local environment without issue, E2Es for iOS are straightforward. Stop any running metro processes. -This was launched previously with `npm run native start:reset`. +If we know we can run the iOS local environment without issue, E2Es for iOS are straightforward. Stop any running metro processes. This was launched previously with `npm run native start:reset`. Then in terminal type: @@ -243,7 +253,7 @@ If all things go well, it should look like: Start the virtual device first. Go back to the AVD by clicking on the phone icon, then click the green play button. -<img src="https://developer.wordpress.org/files/2021/10/adv-integration.png" alt="A screenshot of how to start the Android Simulator."> +<img src="https://developer.wordpress.org/files/2021/10/adv-integration.png" width="700px" alt="A screenshot of how to start the Android Simulator."> Make sure no metro processes are running. This was launched previously with `npm run native start:reset`. @@ -261,4 +271,4 @@ npm run native test:e2e:android:local gutenberg-editor-paragraph.test.js After a bit of a wait we should see: -<img src="https://developer.wordpress.org/files/2021/10/CleanShot-2021-10-27-at-15.28.22.png" alt="A screenshot of block editor integration tests in Android Simulator."> +<img src="https://developer.wordpress.org/files/2021/10/CleanShot-2021-10-27-at-15.28.22.png" width="700px" alt="A screenshot of block editor integration tests in Android Simulator."> From a046a618ee1e38cbc472799afbeb01e6604b5913 Mon Sep 17 00:00:00 2001 From: Jos <jostnes@users.noreply.github.com> Date: Wed, 25 May 2022 17:25:00 +0800 Subject: [PATCH 19/32] update expected html for file block (#41300) Co-authored-by: jos <17252150+jostnes@users.noreply.github.com> --- .../react-native-editor/__device-tests__/helpers/test-data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/test-data.js b/packages/react-native-editor/__device-tests__/helpers/test-data.js index b7dcf9267e40d6..99fea16d643fce 100644 --- a/packages/react-native-editor/__device-tests__/helpers/test-data.js +++ b/packages/react-native-editor/__device-tests__/helpers/test-data.js @@ -135,7 +135,7 @@ exports.coverHeightWithRemUnit = `<!-- wp:cover {"customOverlayColor":"#ffffff", <!-- /wp:cover -->`; exports.fileBlockPlaceholder = `<!-- wp:file {"id":3,"href":"https://wordpress.org/latest.zip"} --> -<div class="wp-block-file"><a href="https://wordpress.org/latest.zip">WordPress.zip</a><a href="https://wordpress.org/latest.zip" class="wp-block-file__button" download>Download</a></div> +<div class="wp-block-file"><a href="https://wordpress.org/latest.zip">WordPress.zip</a><a href="https://wordpress.org/latest.zip" class="wp-block-file__button wp-element-button" download>Download</a></div> <!-- /wp:file -->`; exports.audioBlockPlaceholder = `<!-- wp:audio {"id":5} --> From ede6624935f67df415872e5433ab9da3170325d3 Mon Sep 17 00:00:00 2001 From: Jos <jostnes@users.noreply.github.com> Date: Wed, 25 May 2022 13:47:27 +0800 Subject: [PATCH 20/32] Add waitForVisible() to all blocks (#41126) * add waits to all block - 1st try * fix failing tests * correct if else condition * fix failing cover block test * update spaces * update to use new click helper * wait for blocks to be visible first in getFirstBlock and getLastBlock * remove length as a parameter * update timing for long press since it's failing intermittently in ci * remove deleted param, revert space changes * remove redundant code * exit function once condition is met * increse wait time for long press * remove unneccesary condition, update message, return click value Co-authored-by: jos <17252150+jostnes@users.noreply.github.com> --- .../gutenberg-editor-block-insertion.test.js | 7 - .../gutenberg-editor-cover.test.js | 90 +++------- .../gutenberg-editor-image-@canary.test.js | 6 +- .../gutenberg-editor-search.test.js | 2 + .../gutenberg-editor-spacer.test.js | 4 +- ...utenberg-editor-unsupported-blocks.test.js | 5 +- .../__device-tests__/helpers/utils.js | 75 ++++++-- .../__device-tests__/pages/editor-page.js | 167 ++++++++++-------- 8 files changed, 174 insertions(+), 182 deletions(-) diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js index ead25e7bee9a2e..4dfbf56f52a5a5 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-block-insertion.test.js @@ -39,9 +39,6 @@ describe( 'Gutenberg Editor tests for Block insertion', () => { testData.blockInsertionHtml.toLowerCase() ); - // wait for the block editor to load and for accessibility ids to update - await editorPage.driver.sleep( 3000 ); - // Workaround for now since deleting the first element causes a crash on CI for Android if ( isAndroid() ) { paragraphBlockElement = await editorPage.getTextBlockAtPosition( @@ -55,8 +52,6 @@ describe( 'Gutenberg Editor tests for Block insertion', () => { await paragraphBlockElement.click(); await editorPage.removeBlockAtPosition( blockNames.paragraph, 3 ); for ( let i = 3; i > 0; i-- ) { - // wait for accessibility ids to update - await editorPage.driver.sleep( 1000 ); paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph, i, @@ -72,8 +67,6 @@ describe( 'Gutenberg Editor tests for Block insertion', () => { } } else { for ( let i = 4; i > 0; i-- ) { - // wait for accessibility ids to update - await editorPage.driver.sleep( 1000 ); paragraphBlockElement = await editorPage.getTextBlockAtPosition( blockNames.paragraph ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js index 8d6eaa429a9e80..4905936f7f8f3c 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-cover.test.js @@ -2,95 +2,49 @@ * Internal dependencies */ import { blockNames } from './pages/editor-page'; -import { isAndroid, waitForVisible } from './helpers/utils'; +import { isAndroid } from './helpers/utils'; import testData from './helpers/test-data'; describe( 'Gutenberg Editor Cover Block test', () => { it( 'should displayed properly and have properly converted height (ios only)', async () => { - await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); - - const coverBlock = await editorPage.getBlockAtPosition( - blockNames.cover, - 1, - { useWaitForVisible: true } - ); - // Temporarily this test is skipped on Android,due to the inconsistency of the results, // which are related to getting values in raw pixels instead of density pixels on Android. /* eslint-disable jest/no-conditional-expect */ if ( ! isAndroid() ) { + await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); + + const coverBlock = await editorPage.getBlockAtPosition( + blockNames.cover + ); + const { height } = await coverBlock.getSize(); // Height is set to 20rem, where 1rem is 16. // There is also block's vertical padding equal 32. // Finally, the total height should be 20 * 16 + 32 = 352. expect( height ).toBe( 352 ); - } - /* eslint-enable jest/no-conditional-expect */ + /* eslint-enable jest/no-conditional-expect */ - await coverBlock.click(); - expect( coverBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( blockNames.cover ); + await coverBlock.click(); + expect( coverBlock ).toBeTruthy(); + await editorPage.removeBlockAtPosition( blockNames.cover ); + } } ); // Testing this for iOS on a device is valuable to ensure that it properly // handles opening multiple modals, as only one can be open at a time. it( 'allows modifying media from within block settings', async () => { - await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); - - const coverBlock = await editorPage.getBlockAtPosition( - blockNames.cover, - 1, - { useWaitForVisible: true } - ); - await coverBlock.click(); - // Can only add image from media library on iOS if ( ! isAndroid() ) { - // Open block settings. - const settingsButton = await editorPage.driver.elementByAccessibilityId( - 'Open Settings' - ); - await settingsButton.click(); + await editorPage.setHtmlContent( testData.coverHeightWithRemUnit ); - // Add initial media via button within bottom sheet. - const mediaSection = await editorPage.driver.elementByAccessibilityId( - 'Media Add image or video' - ); - const addMediaButton = await mediaSection.elementByAccessibilityId( - 'Add image or video' + const coverBlock = await editorPage.getBlockAtPosition( + blockNames.cover ); - await addMediaButton.click(); - await editorPage.chooseMediaLibrary(); - await editorPage.driver.sleep( 2000 ); // Await media load. - // Get Edit image button of block - const editImageButtonLocator = - '//XCUIElementTypeButton[@name="Edit image"][@enabled="true"]'; - const blockEditImageButton = await waitForVisible( - editorPage.driver, - editImageButtonLocator - ); - - // Edit media within block settings. - await settingsButton.click(); - await editorPage.driver.sleep( 2000 ); // Await media load. - - // Get Edit image button of block settings. - // NOTE: Since we have multiple Edit image buttons at this - // point, we have to filter them to obtain the correct one. - const settingsEditImageButtons = await editorPage.driver.elementsByXPath( - editImageButtonLocator - ); - const settingsEditImageButton = settingsEditImageButtons.find( - ( element ) => element.value !== blockEditImageButton.value - ); - await settingsEditImageButton.click(); - - // Replace image. - const replaceButton = await editorPage.driver.elementByAccessibilityId( - 'Replace' - ); - await replaceButton.click(); + await editorPage.openBlockSettings( coverBlock ); + await editorPage.clickAddMediaFromCoverBlock(); + await editorPage.chooseMediaLibrary(); + await editorPage.replaceMediaImage(); // First modal should no longer be presented. const replaceButtons = await editorPage.driver.elementsByAccessibilityId( @@ -101,9 +55,9 @@ describe( 'Gutenberg Editor Cover Block test', () => { // Select different media. await editorPage.chooseMediaLibrary(); - } - expect( coverBlock ).toBeTruthy(); - await editorPage.removeBlockAtPosition( blockNames.cover ); + expect( coverBlock ).toBeTruthy(); + await editorPage.removeBlockAtPosition( blockNames.cover ); + } } ); } ); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js index 03a929d5c12094..d07e8a3ea2cb26 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-image-@canary.test.js @@ -11,11 +11,7 @@ describe( 'Gutenberg Editor Image Block tests', () => { await editorPage.closePicker(); const imageBlock = await editorPage.getBlockAtPosition( - blockNames.image, - 1, - { - useWaitForVisible: true, - } + blockNames.image ); // Can only add image from media library on iOS diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js index 7387e4b7125bc1..2233b56f6c37da 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-search.test.js @@ -124,6 +124,7 @@ describe( 'Gutenberg Editor Search Block tests.', () => { searchBlock, 'Button inside' ); + await editorPage.isSearchSettingsVisible(); await editorPage.dismissBottomSheet(); // Switch to html and verify. @@ -141,6 +142,7 @@ describe( 'Gutenberg Editor Search Block tests.', () => { searchBlock, 'No button' ); + await editorPage.isSearchSettingsVisible(); await editorPage.dismissBottomSheet(); // Switch to html and verify. diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js index d234e9ae1dcae5..721ae86ea7eee4 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-spacer.test.js @@ -7,9 +7,7 @@ describe( 'Gutenberg Editor Spacer Block test', () => { it( 'should be able to add a spacer block', async () => { await editorPage.addNewBlock( blockNames.spacer ); const spacerBlock = await editorPage.getBlockAtPosition( - blockNames.spacer, - 1, - { useWaitForVisible: true } + blockNames.spacer ); expect( spacerBlock ).toBeTruthy(); diff --git a/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js b/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js index 6e1be2a13dddb6..e2ae927d4f9449 100644 --- a/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js +++ b/packages/react-native-editor/__device-tests__/gutenberg-editor-unsupported-blocks.test.js @@ -16,8 +16,7 @@ describe( 'Gutenberg Editor Unsupported Block Editor Tests', () => { const editButton = await editorPage.getUnsupportedBlockBottomSheetEditButton(); await editButton.click(); - await expect( - editorPage.getUnsupportedBlockWebView() - ).resolves.toBeTruthy(); + const webView = await editorPage.getUnsupportedBlockWebView(); + await expect( webView ).toBeTruthy(); } ); } ); diff --git a/packages/react-native-editor/__device-tests__/helpers/utils.js b/packages/react-native-editor/__device-tests__/helpers/utils.js index 1658d553c9c7a2..b3225a03cfbae0 100644 --- a/packages/react-native-editor/__device-tests__/helpers/utils.js +++ b/packages/react-native-editor/__device-tests__/helpers/utils.js @@ -310,7 +310,8 @@ const longPressMiddleOfElement = async ( driver, element ) => { const x = location.x + size.width / 2; const y = location.y + size.height / 2; action.press( { x, y } ); - action.wait( 2000 ); + // Setting to wait a bit longer because this is failing more frequently on the CI + action.wait( 5000 ); action.release(); await action.perform(); }; @@ -419,24 +420,28 @@ const toggleHtmlMode = async ( driver, toggleOn ) => { const showHtmlButtonXpath = '/hierarchy/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.ListView/android.widget.TextView[9]'; - const showHtmlButton = await driver.elementByXPath( - showHtmlButtonXpath + + await clickIfClickable( driver, showHtmlButtonXpath ); + } else if ( toggleOn ) { + await clickIfClickable( + driver, + '//XCUIElementTypeButton[@name="..."]' + ); + await clickIfClickable( + driver, + '//XCUIElementTypeButton[@name="Switch to HTML"]' ); - await showHtmlButton.click(); } else { - const menuButton = await driver.elementByAccessibilityId( '...' ); - await menuButton.click(); - let toggleHtmlButton; - if ( toggleOn ) { - toggleHtmlButton = await driver.elementByAccessibilityId( - 'Switch to HTML' - ); - } else { - toggleHtmlButton = await driver.elementByAccessibilityId( - 'Switch To Visual' - ); - } - await toggleHtmlButton.click(); + // This is to wait for the clipboard paste notification to disappear, currently it overlaps with the menu button + await driver.sleep( 3000 ); + await clickIfClickable( + driver, + '//XCUIElementTypeButton[@name="..."]' + ); + await clickIfClickable( + driver, + '//XCUIElementTypeButton[@name="Switch To Visual"]' + ); } }; @@ -492,7 +497,7 @@ const waitForVisible = async ( } const element = await driver.elementsByXPath( elementLocator ); - if ( element.length !== 1 ) { + if ( element.length === 0 ) { // if locator is not visible, try again return waitForVisible( driver, @@ -530,6 +535,39 @@ const isElementVisible = async ( return true; }; +const clickIfClickable = async ( + driver, + elementLocator, + maxIteration = 25, + iteration = 0 +) => { + const element = await waitForVisible( + driver, + elementLocator, + maxIteration, + iteration + ); + + try { + return await element.click(); + } catch ( error ) { + if ( iteration >= maxIteration ) { + // eslint-disable-next-line no-console + console.error( + `"${ elementLocator }" still not clickable after "${ iteration }" retries` + ); + return ''; + } + + return clickIfClickable( + driver, + elementLocator, + maxIteration, + iteration + 1 + ); + } +}; + // Only for Android const waitIfAndroid = async () => { if ( isAndroid() ) { @@ -540,6 +578,7 @@ const waitIfAndroid = async () => { module.exports = { backspace, clickBeginningOfElement, + clickIfClickable, clickMiddleOfElement, doubleTap, isAndroid, diff --git a/packages/react-native-editor/__device-tests__/pages/editor-page.js b/packages/react-native-editor/__device-tests__/pages/editor-page.js index 440e256566ef91..f7e69057ece2a2 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -15,6 +15,7 @@ const { toggleHtmlMode, typeString, waitForVisible, + clickIfClickable, } = require( '../helpers/utils' ); const initializeEditorPage = async () => { @@ -79,31 +80,24 @@ class EditorPage { async getBlockAtPosition( blockName, position = 1, - options = { autoscroll: false, useWaitForVisible: false } + options = { autoscroll: false } ) { - let blockLocator; - - // Make it optional to use waitForVisible() so we can handle this test by test. - // This condition can be removed once we have gone through all test cases. - if ( options.useWaitForVisible ) { - let elementType; - switch ( blockName ) { - case blockNames.cover: - elementType = 'XCUIElementTypeButton'; - break; - default: - elementType = 'XCUIElementTypeOther'; - break; - } + let elementType; + switch ( blockName ) { + case blockNames.cover: + elementType = 'XCUIElementTypeButton'; + break; + default: + elementType = 'XCUIElementTypeOther'; + break; + } - blockLocator = isAndroid() - ? `//android.view.ViewGroup[contains(@${ this.accessibilityIdXPathAttrib }, "${ blockName } Block. Row ${ position }")]` - : `(//${ elementType }[contains(@${ this.accessibilityIdXPathAttrib }, "${ blockName } Block. Row ${ position }")])[1]`; + const blockLocator = isAndroid() + ? `//android.view.ViewGroup[contains(@${ this.accessibilityIdXPathAttrib }, "${ blockName } Block. Row ${ position }")]` + : `(//${ elementType }[contains(@${ this.accessibilityIdXPathAttrib }, "${ blockName } Block. Row ${ position }")])[1]`; + + await waitForVisible( this.driver, blockLocator ); - await waitForVisible( this.driver, blockLocator ); - } else { - blockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, "${ blockName } Block. Row ${ position }")]`; - } const elements = await this.driver.elementsByXPath( blockLocator ); const lastElementFound = elements[ elements.length - 1 ]; if ( elements.length === 0 && options.autoscroll ) { @@ -151,12 +145,12 @@ class EditorPage { async getFirstBlockVisible() { const firstBlockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, " Block. Row ")]`; - const elements = await this.driver.elementsByXPath( firstBlockLocator ); - return elements[ 0 ]; + return await waitForVisible( this.driver, firstBlockLocator ); } async getLastBlockVisible() { const firstBlockLocator = `//*[contains(@${ this.accessibilityIdXPathAttrib }, " Block. Row ")]`; + await waitForVisible( this.driver, firstBlockLocator ); const elements = await this.driver.elementsByXPath( firstBlockLocator ); return elements[ elements.length - 1 ]; } @@ -164,9 +158,7 @@ class EditorPage { async hasBlockAtPosition( position = 1, blockName = '' ) { return ( undefined !== - ( await this.getBlockAtPosition( blockName, position, { - useWaitForVisible: true, - } ) ) + ( await this.getBlockAtPosition( blockName, position ) ) ); } @@ -252,19 +244,18 @@ class EditorPage { // Sometimes double tap is not enough for paste menu to appear, so we also long press. await longPressMiddleOfElement( this.driver, htmlContentView ); - const pasteButton = this.driver.elementByXPath( + const pasteButton = await waitForVisible( + this.driver, '//XCUIElementTypeMenuItem[@name="Paste"]' ); await pasteButton.click(); - await this.driver.sleep( 3000 ); // Wait for paste notification to disappear. } await toggleHtmlMode( this.driver, false ); } async dismissKeyboard() { - await this.driver.sleep( 1000 ); // Wait for any keyboard animations. const keyboardShown = await this.driver.isKeyboardShown(); if ( ! keyboardShown ) { return; @@ -325,11 +316,7 @@ class EditorPage { ? '//android.widget.Button[@content-desc="Add Block Before"]' : '//XCUIElementTypeButton[@name="Add Block Before"]'; - const addBlockBeforeButton = await waitForVisible( - this.driver, - addBlockBeforeButtonLocator - ); - await addBlockBeforeButton.click(); + await clickIfClickable( this.driver, addBlockBeforeButtonLocator ); } else { await addButton.click(); } @@ -486,9 +473,7 @@ class EditorPage { blockActionsMenuButtonLocator ); if ( isAndroid() ) { - const block = await this.getBlockAtPosition( blockName, position, { - useWaitForVisible: true, - } ); + const block = await this.getBlockAtPosition( blockName, position ); let checkList = await this.driver.elementsByXPath( blockActionsMenuButtonLocator ); @@ -572,20 +557,17 @@ class EditorPage { ) { // iOS needs a few extra steps to get the text element if ( ! isAndroid() ) { - // Wait for and click the list in the correct position - let listBlock = await waitForVisible( + // Click the list in the correct position + await clickIfClickable( this.driver, `(//XCUIElementTypeOther[contains(@name, "List Block. Row ${ position }")])[1]` ); - await listBlock.click(); const listBlockLocator = options.isEmptyBlock ? `(//XCUIElementTypeStaticText[contains(@name, "List")])` : `//XCUIElementTypeButton[contains(@name, "List")]`; - // Wait for and click the list to get the text element - listBlock = await waitForVisible( this.driver, listBlockLocator ); - await listBlock.click(); + await clickIfClickable( this.driver, listBlockLocator ); } const listBlockTextLocatorIOS = options.isEmptyBlock @@ -608,6 +590,33 @@ class EditorPage { await this.clickToolBarButton( this.orderedListButtonName ); } + // ========================= + // Cover Block functions + // For iOS only + // ========================= + + async clickAddMediaFromCoverBlock() { + const mediaSection = await waitForVisible( + this.driver, + '//XCUIElementTypeOther[@name="Media Add image or video"]' + ); + const addMediaButton = await mediaSection.elementByAccessibilityId( + 'Add image or video' + ); + await addMediaButton.click(); + } + + async replaceMediaImage() { + await clickIfClickable( + this.driver, + '(//XCUIElementTypeButton[@name="Edit image"])[1]' + ); + await clickIfClickable( + this.driver, + '//XCUIElementTypeButton[@name="Replace"]' + ); + } + // ========================= // Image Block functions // ========================= @@ -687,10 +696,12 @@ class EditorPage { const elementName = isAndroid() ? '//*' : '//XCUIElementTypeOther'; const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Hide search heading")]`; - return await this.driver - .elementByXPath( locator ) - .click() - .sleep( isAndroid() ? 200 : 0 ); + const hideSearchHeadingToggle = await waitForVisible( + this.driver, + locator + ); + + return await hideSearchHeadingToggle.click(); } async changeSearchButtonPositionSetting( block, buttonPosition ) { @@ -699,13 +710,16 @@ class EditorPage { const elementName = isAndroid() ? '//*' : '//XCUIElementTypeButton'; const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Button position")]`; - await this.driver.elementByXPath( locator ).click(); + let optionMenuButton = await waitForVisible( this.driver, locator ); + await optionMenuButton.click(); const optionMenuButtonLocator = `${ elementName }[contains(@${ this.accessibilityIdXPathAttrib }, "${ buttonPosition }")]`; - return await this.driver - .elementByXPath( optionMenuButtonLocator ) - .click() - .sleep( isAndroid() ? 600 : 200 ); // sleep a little longer due to multiple menus. + optionMenuButton = await waitForVisible( + this.driver, + optionMenuButtonLocator + ); + + return await optionMenuButton.click(); } async toggleSearchIconOnlySetting( block ) { @@ -714,10 +728,16 @@ class EditorPage { const elementName = isAndroid() ? '//*' : '//XCUIElementTypeOther'; const locator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Use icon button")]`; - return await this.driver - .elementByXPath( locator ) - .click() - .sleep( isAndroid() ? 200 : 0 ); + const useIconButton = await waitForVisible( this.driver, locator ); + + return await useIconButton.click(); + } + + async isSearchSettingsVisible() { + const elementName = isAndroid() ? '//*' : '//XCUIElementTypeButton'; + const buttonPositionLocator = `${ elementName }[starts-with(@${ this.accessibilityIdXPathAttrib }, "Button position")]`; + + return await waitForVisible( this.driver, buttonPositionLocator ); } // ============================= @@ -726,37 +746,28 @@ class EditorPage { async getUnsupportedBlockHelpButton() { const accessibilityId = 'Help button'; - let blockLocator = - '//android.widget.Button[@content-desc="Help button, Tap here to show help"]'; + const blockLocator = isAndroid() + ? `//android.widget.Button[starts-with(@content-desc, "${ accessibilityId }")]` + : `//XCUIElementTypeButton[@name="${ accessibilityId }"]`; - if ( ! isAndroid() ) { - blockLocator = `//XCUIElementTypeButton[@name="${ accessibilityId }"]`; - } - return await this.driver.elementByXPath( blockLocator ); + return await waitForVisible( this.driver, blockLocator ); } async getUnsupportedBlockBottomSheetEditButton() { const accessibilityId = 'Edit using web editor'; - let blockLocator = - '//android.widget.Button[@content-desc="Edit using web editor"]'; + const blockLocator = isAndroid() + ? `//android.widget.Button[@content-desc="${ accessibilityId }"]` + : `//XCUIElementTypeButton[@name="${ accessibilityId }"]`; - if ( ! isAndroid() ) { - blockLocator = `//XCUIElementTypeButton[@name="${ accessibilityId }"]`; - } - return await this.driver.elementByXPath( blockLocator ); + return await waitForVisible( this.driver, blockLocator ); } async getUnsupportedBlockWebView() { - let blockLocator = '//android.webkit.WebView'; - - if ( ! isAndroid() ) { - blockLocator = '//XCUIElementTypeWebView'; - } + const blockLocator = isAndroid() + ? '//android.webkit.WebView' + : '//XCUIElementTypeWebView'; - this.driver.setImplicitWaitTimeout( 20000 ); - const element = await this.driver.elementByXPath( blockLocator ); - this.driver.setImplicitWaitTimeout( 5000 ); - return element; + return await waitForVisible( this.driver, blockLocator ); } async stopDriver() { From 2daf775b8a435ed4862b126c1488e1da3e3f7ade Mon Sep 17 00:00:00 2001 From: Siobhan <siobhan@automattic.com> Date: Thu, 26 May 2022 09:52:36 +0100 Subject: [PATCH 21/32] Update CHANGELOG --- packages/react-native-editor/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 9b3a6cf719b568..6e1eecd42563c0 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -14,6 +14,7 @@ For each user feature we should also add a importance categorization label to i ## 1.77.0 - [*] [a11y] Improve text read by screen readers for BottomSheetSelectControl [#41036] +- [*] Add 'Insert from URL' option to Image block [#40334] ## 1.76.1 From d8fbfbdbb19e52adec4fde126b6b9b087694b186 Mon Sep 17 00:00:00 2001 From: Siobhan <siobhan@automattic.com> Date: Thu, 26 May 2022 09:57:29 +0100 Subject: [PATCH 22/32] Revert "update expected html for file block (#41300)" This reverts commit a046a618ee1e38cbc472799afbeb01e6604b5913. --- .../react-native-editor/__device-tests__/helpers/test-data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-editor/__device-tests__/helpers/test-data.js b/packages/react-native-editor/__device-tests__/helpers/test-data.js index 99fea16d643fce..b7dcf9267e40d6 100644 --- a/packages/react-native-editor/__device-tests__/helpers/test-data.js +++ b/packages/react-native-editor/__device-tests__/helpers/test-data.js @@ -135,7 +135,7 @@ exports.coverHeightWithRemUnit = `<!-- wp:cover {"customOverlayColor":"#ffffff", <!-- /wp:cover -->`; exports.fileBlockPlaceholder = `<!-- wp:file {"id":3,"href":"https://wordpress.org/latest.zip"} --> -<div class="wp-block-file"><a href="https://wordpress.org/latest.zip">WordPress.zip</a><a href="https://wordpress.org/latest.zip" class="wp-block-file__button wp-element-button" download>Download</a></div> +<div class="wp-block-file"><a href="https://wordpress.org/latest.zip">WordPress.zip</a><a href="https://wordpress.org/latest.zip" class="wp-block-file__button" download>Download</a></div> <!-- /wp:file -->`; exports.audioBlockPlaceholder = `<!-- wp:audio {"id":5} --> From efc0f2c8510cc33f336c5d80389a686e216d5601 Mon Sep 17 00:00:00 2001 From: Carlos Garcia <fluiddot@gmail.com> Date: Thu, 26 May 2022 15:43:58 +0200 Subject: [PATCH 23/32] [RNMobile] Ensure post title gets focused when is notified from native side (#41371) --- packages/edit-post/src/editor.native.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/edit-post/src/editor.native.js b/packages/edit-post/src/editor.native.js index cf4eaa53389650..20e4585307ed31 100644 --- a/packages/edit-post/src/editor.native.js +++ b/packages/edit-post/src/editor.native.js @@ -92,6 +92,9 @@ class Editor extends Component { () => { if ( this.postTitleRef ) { this.postTitleRef.focus(); + } else { + // If the post title ref is not available, we postpone setting focus to when it's available. + this.focusTitleWhenAvailable = true; } } ); @@ -122,6 +125,11 @@ class Editor extends Component { } setTitleRef( titleRef ) { + if ( this.focusTitleWhenAvailable && ! this.postTitleRef ) { + this.focusTitleWhenAvailable = false; + titleRef.focus(); + } + this.postTitleRef = titleRef; } From 4c714af7f0cb928e97af26404abefb8c7db1a38e Mon Sep 17 00:00:00 2001 From: Carlos Garcia <fluiddot@gmail.com> Date: Thu, 2 Jun 2022 11:11:56 +0200 Subject: [PATCH 24/32] Release script: Update react-native-editor version to 1.77.1 --- packages/react-native-aztec/package.json | 2 +- packages/react-native-bridge/package.json | 2 +- packages/react-native-editor/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native-aztec/package.json b/packages/react-native-aztec/package.json index 1d6c079cfbf0c2..9eedd000b067b1 100644 --- a/packages/react-native-aztec/package.json +++ b/packages/react-native-aztec/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-aztec", - "version": "1.77.0", + "version": "1.77.1", "description": "Aztec view for react-native.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-bridge/package.json b/packages/react-native-bridge/package.json index 525cbcfbc2465f..73dee5559892f4 100644 --- a/packages/react-native-bridge/package.json +++ b/packages/react-native-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-bridge", - "version": "1.77.0", + "version": "1.77.1", "description": "Native bridge library used to integrate the block editor into a native App.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index c02f10c28a5ef3..a4e5335c4c6c9b 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-editor", - "version": "1.77.0", + "version": "1.77.1", "description": "Mobile WordPress gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From 9365c4ec70c4ff329d51de623c6c0ed8eaa8b4d4 Mon Sep 17 00:00:00 2001 From: Carlos Garcia <fluiddot@gmail.com> Date: Thu, 2 Jun 2022 11:12:34 +0200 Subject: [PATCH 25/32] Release script: Update with changes from 'npm run core preios' --- packages/react-native-editor/ios/Podfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-native-editor/ios/Podfile.lock b/packages/react-native-editor/ios/Podfile.lock index d12d4b360b34c6..902c5377a6ca4a 100644 --- a/packages/react-native-editor/ios/Podfile.lock +++ b/packages/react-native-editor/ios/Podfile.lock @@ -13,7 +13,7 @@ PODS: - ReactCommon/turbomodule/core (= 0.66.2) - fmt (6.2.1) - glog (0.3.5) - - Gutenberg (1.77.0): + - Gutenberg (1.77.1): - React-Core (= 0.66.2) - React-CoreModules (= 0.66.2) - React-RCTImage (= 0.66.2) @@ -337,7 +337,7 @@ PODS: - React-Core - RNSVG (9.13.6): - React-Core - - RNTAztecView (1.77.0): + - RNTAztecView (1.77.1): - React-Core - WordPress-Aztec-iOS (~> 1.19.8) - WordPress-Aztec-iOS (1.19.8) @@ -503,7 +503,7 @@ SPEC CHECKSUMS: FBReactNativeSpec: 18438b1c04ce502ed681cd19db3f4508964c082a fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 5337263514dd6f09803962437687240c5dc39aa4 - Gutenberg: 7f0a288f26e36807c8e27f97b3a6ff929310eea3 + Gutenberg: 66dc1842f9cfad675624e28cfeedf6517d1c42a3 RCT-Folly: a21c126816d8025b547704b777a2ba552f3d9fa9 RCTRequired: 5e9e85f48da8dd447f5834ce14c6799ea8c7f41a RCTTypeSafety: aba333d04d88d1f954e93666a08d7ae57a87ab30 @@ -542,7 +542,7 @@ SPEC CHECKSUMS: RNReanimated: d87c75f1076bab3402d6cd0b7322be51d333d10e RNScreens: 953633729a42e23ad0c93574d676b361e3335e8b RNSVG: 36a7359c428dcb7c6bce1cc546fbfebe069809b0 - RNTAztecView: 2524811d1168d1f5148220ec8ea3c012ec8f059c + RNTAztecView: 4d1509eb51c287d059942c311fe1ae7d445e3653 WordPress-Aztec-iOS: 7d11d598f14c82c727c08b56bd35fbeb7dafb504 Yoga: 9a08effa851c1d8cc1647691895540bc168ea65f From ad2c8e43a42b368e8e39fe5e0465a7c4d9102ce9 Mon Sep 17 00:00:00 2001 From: Carlos Garcia <fluiddot@gmail.com> Date: Thu, 2 Jun 2022 11:44:51 +0200 Subject: [PATCH 26/32] [RNMobile] Bump `react-native-reanimated` version to `2.4.1-wp-3` (#41482) --- package-lock.json | 6 +++--- packages/react-native-editor/ios/Podfile.lock | 4 ++-- packages/react-native-editor/package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 37baac458da62a..d11ca8d655a9be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18231,7 +18231,7 @@ "react-native-linear-gradient": "https://raw.githubusercontent.com/wordpress-mobile/react-native-linear-gradient/v2.5.6-wp-2/react-native-linear-gradient-2.5.6-wp-2.tgz", "react-native-modal": "^11.10.0", "react-native-prompt-android": "https://raw.githubusercontent.com/wordpress-mobile/react-native-prompt-android/v1.0.0-wp-2/react-native-prompt-android-1.0.0-wp-2.tgz", - "react-native-reanimated": "https://raw.githubusercontent.com/wordpress-mobile/react-native-reanimated/2.4.1-wp-2/react-native-reanimated-2.4.1-wp-2.tgz", + "react-native-reanimated": "https://raw.githubusercontent.com/wordpress-mobile/react-native-reanimated/2.4.1-wp-3/react-native-reanimated-2.4.1-wp-3.tgz", "react-native-safe-area": "^0.5.0", "react-native-safe-area-context": "3.2.0", "react-native-sass-transformer": "^1.1.1", @@ -50931,8 +50931,8 @@ "integrity": "sha512-9whL4Kc5OU5Q89Dneq8oT8vpQTA/cEz24EIPXEQ2KGo1Dkf4qzer5+98YXJM2F8yitCP8UKHOL8WIiE7zukXBA==" }, "react-native-reanimated": { - "version": "https://raw.githubusercontent.com/wordpress-mobile/react-native-reanimated/2.4.1-wp-2/react-native-reanimated-2.4.1-wp-2.tgz", - "integrity": "sha512-8Mu7150ezI5PGBYAatqhQlau0nkeXMVNZIODAU7l1e7qjfEALZiuxKMkvWhFw1xBCqx+qRv24yYns7I5GGiZGQ==", + "version": "https://raw.githubusercontent.com/wordpress-mobile/react-native-reanimated/2.4.1-wp-3/react-native-reanimated-2.4.1-wp-3.tgz", + "integrity": "sha512-LnfbSRe9WZIj/LT9ZrtiDKCjEqCPp+wcugBIUCgfb6przB3dwrCOiFdxZBD+Py58h6wN7fVpIOhPaP5rGQ1TQQ==", "requires": { "@babel/plugin-transform-object-assign": "^7.10.4", "@types/invariant": "^2.2.35", diff --git a/packages/react-native-editor/ios/Podfile.lock b/packages/react-native-editor/ios/Podfile.lock index 902c5377a6ca4a..0516c50d01d805 100644 --- a/packages/react-native-editor/ios/Podfile.lock +++ b/packages/react-native-editor/ios/Podfile.lock @@ -306,7 +306,7 @@ PODS: - React-Core - RNGestureHandler (2.2.0-wp-4): - React-Core - - RNReanimated (2.4.1-wp-2): + - RNReanimated (2.4.1-wp-3): - DoubleConversion - FBLazyVector - FBReactNativeSpec @@ -539,7 +539,7 @@ SPEC CHECKSUMS: RNCClipboard: 99fc8ad669a376b756fbc8098ae2fd05c0ed0668 RNCMaskedView: c298b644a10c0c142055b3ae24d83879ecb13ccd RNGestureHandler: 93b98c40b9419b1a82b008b513c182fe09288d1f - RNReanimated: d87c75f1076bab3402d6cd0b7322be51d333d10e + RNReanimated: b413cc7aa3e2a740d9804cda3a9396a68f9eea7f RNScreens: 953633729a42e23ad0c93574d676b361e3335e8b RNSVG: 36a7359c428dcb7c6bce1cc546fbfebe069809b0 RNTAztecView: 4d1509eb51c287d059942c311fe1ae7d445e3653 diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index a4e5335c4c6c9b..f1df15834b67dd 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -65,7 +65,7 @@ "react-native-linear-gradient": "https://raw.githubusercontent.com/wordpress-mobile/react-native-linear-gradient/v2.5.6-wp-2/react-native-linear-gradient-2.5.6-wp-2.tgz", "react-native-modal": "^11.10.0", "react-native-prompt-android": "https://raw.githubusercontent.com/wordpress-mobile/react-native-prompt-android/v1.0.0-wp-2/react-native-prompt-android-1.0.0-wp-2.tgz", - "react-native-reanimated": "https://raw.githubusercontent.com/wordpress-mobile/react-native-reanimated/2.4.1-wp-2/react-native-reanimated-2.4.1-wp-2.tgz", + "react-native-reanimated": "https://raw.githubusercontent.com/wordpress-mobile/react-native-reanimated/2.4.1-wp-3/react-native-reanimated-2.4.1-wp-3.tgz", "react-native-safe-area": "^0.5.0", "react-native-safe-area-context": "3.2.0", "react-native-sass-transformer": "^1.1.1", From f10a378f254793402fb9837b9414765235f36e19 Mon Sep 17 00:00:00 2001 From: Carlos Garcia <fluiddot@gmail.com> Date: Thu, 2 Jun 2022 11:47:44 +0200 Subject: [PATCH 27/32] Update react-native-editor changelog --- packages/react-native-editor/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 6e1eecd42563c0..6dde09c5afd547 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -11,6 +11,10 @@ For each user feature we should also add a importance categorization label to i ## Unreleased +## 1.77.1 + +- [***] Fixes a crash on iOS related to JSI and Reanimated [#41482] + ## 1.77.0 - [*] [a11y] Improve text read by screen readers for BottomSheetSelectControl [#41036] From 4cfbd825072365698f10e3c2acbaeff1b6eb177a Mon Sep 17 00:00:00 2001 From: Carlos Garcia <fluiddot@gmail.com> Date: Fri, 10 Jun 2022 10:20:53 +0200 Subject: [PATCH 28/32] Release script: Update react-native-editor version to 1.78.0 --- packages/react-native-aztec/package.json | 2 +- packages/react-native-bridge/package.json | 2 +- packages/react-native-editor/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native-aztec/package.json b/packages/react-native-aztec/package.json index 9eedd000b067b1..8a2d54acbf4e36 100644 --- a/packages/react-native-aztec/package.json +++ b/packages/react-native-aztec/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-aztec", - "version": "1.77.1", + "version": "1.78.0", "description": "Aztec view for react-native.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-bridge/package.json b/packages/react-native-bridge/package.json index 73dee5559892f4..969cdd6a53e9a1 100644 --- a/packages/react-native-bridge/package.json +++ b/packages/react-native-bridge/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-bridge", - "version": "1.77.1", + "version": "1.78.0", "description": "Native bridge library used to integrate the block editor into a native App.", "private": true, "author": "The WordPress Contributors", diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index f1df15834b67dd..d252432fe2d98d 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/react-native-editor", - "version": "1.77.1", + "version": "1.78.0", "description": "Mobile WordPress gutenberg editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", From 5b447b441735f8949b5cf432a39772190c104e07 Mon Sep 17 00:00:00 2001 From: Carlos Garcia <fluiddot@gmail.com> Date: Fri, 10 Jun 2022 10:21:21 +0200 Subject: [PATCH 29/32] Release script: Update with changes from 'npm run core preios' --- packages/react-native-editor/ios/Podfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-native-editor/ios/Podfile.lock b/packages/react-native-editor/ios/Podfile.lock index 0516c50d01d805..7ad8acf1adcad6 100644 --- a/packages/react-native-editor/ios/Podfile.lock +++ b/packages/react-native-editor/ios/Podfile.lock @@ -13,7 +13,7 @@ PODS: - ReactCommon/turbomodule/core (= 0.66.2) - fmt (6.2.1) - glog (0.3.5) - - Gutenberg (1.77.1): + - Gutenberg (1.78.0): - React-Core (= 0.66.2) - React-CoreModules (= 0.66.2) - React-RCTImage (= 0.66.2) @@ -337,7 +337,7 @@ PODS: - React-Core - RNSVG (9.13.6): - React-Core - - RNTAztecView (1.77.1): + - RNTAztecView (1.78.0): - React-Core - WordPress-Aztec-iOS (~> 1.19.8) - WordPress-Aztec-iOS (1.19.8) @@ -503,7 +503,7 @@ SPEC CHECKSUMS: FBReactNativeSpec: 18438b1c04ce502ed681cd19db3f4508964c082a fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 5337263514dd6f09803962437687240c5dc39aa4 - Gutenberg: 66dc1842f9cfad675624e28cfeedf6517d1c42a3 + Gutenberg: 80e1d305a53a6b59409353860691826756d23da4 RCT-Folly: a21c126816d8025b547704b777a2ba552f3d9fa9 RCTRequired: 5e9e85f48da8dd447f5834ce14c6799ea8c7f41a RCTTypeSafety: aba333d04d88d1f954e93666a08d7ae57a87ab30 @@ -542,7 +542,7 @@ SPEC CHECKSUMS: RNReanimated: b413cc7aa3e2a740d9804cda3a9396a68f9eea7f RNScreens: 953633729a42e23ad0c93574d676b361e3335e8b RNSVG: 36a7359c428dcb7c6bce1cc546fbfebe069809b0 - RNTAztecView: 4d1509eb51c287d059942c311fe1ae7d445e3653 + RNTAztecView: 3f363845da564392805047da58552665dc3be95a WordPress-Aztec-iOS: 7d11d598f14c82c727c08b56bd35fbeb7dafb504 Yoga: 9a08effa851c1d8cc1647691895540bc168ea65f From 8bd8d90c697784f136a4c66768e4b018e39ee4c0 Mon Sep 17 00:00:00 2001 From: Carlos Garcia <fluiddot@gmail.com> Date: Fri, 27 May 2022 17:14:35 +0200 Subject: [PATCH 30/32] [RNMobile] Bump `react-native-gesture-handler` to version `2.3.2` (#41337) --- package-lock.json | 6 +++--- .../block-mover/test/__snapshots__/index.native.js.snap | 4 ++++ .../wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java | 2 +- .../app/src/main/java/com/gutenberg/MainApplication.java | 2 +- packages/react-native-editor/ios/Podfile.lock | 4 ++-- packages/react-native-editor/package.json | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index d11ca8d655a9be..00e90e22c63027 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18223,7 +18223,7 @@ "jsdom-jscore-rn": "git+https://github.com/iamcco/jsdom-jscore-rn.git#a562f3d57c27c13e5bfc8cf82d496e69a3ba2800", "node-fetch": "^2.6.0", "react-native": "0.66.2", - "react-native-gesture-handler": "https://raw.githubusercontent.com/wordpress-mobile/react-native-gesture-handler/2.2.0-wp-4/react-native-gesture-handler-2.2.0-wp-4.tgz", + "react-native-gesture-handler": "https://raw.githubusercontent.com/wordpress-mobile/react-native-gesture-handler/2.3.2-wp-1/react-native-gesture-handler-2.3.2-wp-1.tgz", "react-native-get-random-values": "1.4.0", "react-native-hr": "git+https://github.com/Riglerr/react-native-hr.git#2d01a5cf77212d100e8b99e0310cce5234f977b3", "react-native-hsv-color-picker": "https://raw.githubusercontent.com/wordpress-mobile/react-native-hsv-color-picker/v1.0.1-wp-2/react-native-hsv-color-picker-1.0.1-wp-2.tgz", @@ -50870,8 +50870,8 @@ } }, "react-native-gesture-handler": { - "version": "https://raw.githubusercontent.com/wordpress-mobile/react-native-gesture-handler/2.2.0-wp-4/react-native-gesture-handler-2.2.0-wp-4.tgz", - "integrity": "sha512-3pHMTuMWAko1nFJYRRrdqjnEGmPKxPe7C/wRa4PZ+FCelqdXoomwMLp1Rw7q722MXpQbjxQsQplfgPL4X5KxvQ==", + "version": "https://raw.githubusercontent.com/wordpress-mobile/react-native-gesture-handler/2.3.2-wp-1/react-native-gesture-handler-2.3.2-wp-1.tgz", + "integrity": "sha512-XKwFyoU2rsyQVVK9DhygNUsSipHBbqc5o/P/3davbRF81T7uPHwnTf/9X3Jze4eIwfiZmnRLz9rFgVxvK/1JyA==", "requires": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", diff --git a/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap b/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap index 79b79766caaa68..ec1b5b196d57bb 100644 --- a/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap +++ b/packages/block-editor/src/components/block-mover/test/__snapshots__/index.native.js.snap @@ -35,6 +35,8 @@ Array [ > <View collapsable={false} + handlerTag={3} + handlerType="LongPressGestureHandler" onGestureHandlerEvent={[Function]} onGestureHandlerStateChange={[Function]} style={ @@ -93,6 +95,8 @@ Array [ > <View collapsable={false} + handlerTag={4} + handlerType="LongPressGestureHandler" onGestureHandlerEvent={[Function]} onGestureHandlerStateChange={[Function]} style={ diff --git a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java index 86e5a6b63ce2fa..3e4d06bcd935c1 100644 --- a/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java +++ b/packages/react-native-bridge/android/react-native-bridge/src/main/java/org/wordpress/mobile/WPAndroidGlue/WPAndroidGlueCode.java @@ -46,7 +46,7 @@ import org.linusu.RNGetRandomValuesPackage; import com.reactnativecommunity.webview.RNCWebViewPackage; import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView; -import com.swmansion.gesturehandler.react.RNGestureHandlerPackage; +import com.swmansion.gesturehandler.RNGestureHandlerPackage; import com.swmansion.reanimated.ReanimatedJSIModulePackage; import com.swmansion.reanimated.ReanimatedPackage; import com.swmansion.rnscreens.RNScreensPackage; diff --git a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java index d74315aba221a2..b94e581a736cbe 100644 --- a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java +++ b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java @@ -37,7 +37,7 @@ import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; import com.reactnativecommunity.webview.RNCWebViewPackage; -import com.swmansion.gesturehandler.react.RNGestureHandlerPackage; +import com.swmansion.gesturehandler.RNGestureHandlerPackage; import com.swmansion.reanimated.ReanimatedJSIModulePackage; import com.swmansion.reanimated.ReanimatedPackage; import com.swmansion.rnscreens.RNScreensPackage; diff --git a/packages/react-native-editor/ios/Podfile.lock b/packages/react-native-editor/ios/Podfile.lock index 7ad8acf1adcad6..1b37acba4098ab 100644 --- a/packages/react-native-editor/ios/Podfile.lock +++ b/packages/react-native-editor/ios/Podfile.lock @@ -304,7 +304,7 @@ PODS: - React-Core - RNCMaskedView (0.2.6): - React-Core - - RNGestureHandler (2.2.0-wp-4): + - RNGestureHandler (2.3.2-wp-1): - React-Core - RNReanimated (2.4.1-wp-3): - DoubleConversion @@ -538,7 +538,7 @@ SPEC CHECKSUMS: ReactCommon: c0263c1a41509aeb94be3214fa7bc3b71eae5ef6 RNCClipboard: 99fc8ad669a376b756fbc8098ae2fd05c0ed0668 RNCMaskedView: c298b644a10c0c142055b3ae24d83879ecb13ccd - RNGestureHandler: 93b98c40b9419b1a82b008b513c182fe09288d1f + RNGestureHandler: 3b13cc25407d1cdbee33b6ae65790a55c032d2a9 RNReanimated: b413cc7aa3e2a740d9804cda3a9396a68f9eea7f RNScreens: 953633729a42e23ad0c93574d676b361e3335e8b RNSVG: 36a7359c428dcb7c6bce1cc546fbfebe069809b0 diff --git a/packages/react-native-editor/package.json b/packages/react-native-editor/package.json index d252432fe2d98d..8fa80cf8699391 100644 --- a/packages/react-native-editor/package.json +++ b/packages/react-native-editor/package.json @@ -57,7 +57,7 @@ "jsdom-jscore-rn": "git+https://github.com/iamcco/jsdom-jscore-rn.git#a562f3d57c27c13e5bfc8cf82d496e69a3ba2800", "node-fetch": "^2.6.0", "react-native": "0.66.2", - "react-native-gesture-handler": "https://raw.githubusercontent.com/wordpress-mobile/react-native-gesture-handler/2.2.0-wp-4/react-native-gesture-handler-2.2.0-wp-4.tgz", + "react-native-gesture-handler": "https://raw.githubusercontent.com/wordpress-mobile/react-native-gesture-handler/2.3.2-wp-1/react-native-gesture-handler-2.3.2-wp-1.tgz", "react-native-get-random-values": "1.4.0", "react-native-hr": "git+https://github.com/Riglerr/react-native-hr.git#2d01a5cf77212d100e8b99e0310cce5234f977b3", "react-native-hsv-color-picker": "https://raw.githubusercontent.com/wordpress-mobile/react-native-hsv-color-picker/v1.0.1-wp-2/react-native-hsv-color-picker-1.0.1-wp-2.tgz", From bdaaf6e1b1b02bf2ab933fbbbb47284ca681604d Mon Sep 17 00:00:00 2001 From: Carlos Garcia <fluiddot@gmail.com> Date: Fri, 10 Jun 2022 10:55:50 +0200 Subject: [PATCH 31/32] Update react-native-editor changelog --- packages/react-native-editor/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index 6dde09c5afd547..0a7190341a464b 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -11,9 +11,13 @@ For each user feature we should also add a importance categorization label to i ## Unreleased +## 1.78.0 + +- [*] Bump react-native-gesture-handler to version 2.3.2 [#41337] + ## 1.77.1 -- [***] Fixes a crash on iOS related to JSI and Reanimated [#41482] +- [***] Fix crash on iOS related to JSI and Reanimated [#41482] ## 1.77.0 From 6fbe7cb4467f4427eee9adab628d10dc0d81eea0 Mon Sep 17 00:00:00 2001 From: Carlos Garcia <fluiddot@gmail.com> Date: Mon, 13 Jun 2022 10:19:00 +0200 Subject: [PATCH 32/32] Remove react-native-editor changelog duplicate entries --- packages/react-native-editor/CHANGELOG.md | 31 +++++------------------ 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/packages/react-native-editor/CHANGELOG.md b/packages/react-native-editor/CHANGELOG.md index c2012ba54c6d31..1491db6746b626 100644 --- a/packages/react-native-editor/CHANGELOG.md +++ b/packages/react-native-editor/CHANGELOG.md @@ -10,11 +10,16 @@ For each user feature we should also add a importance categorization label to i --> ## Unreleased + - [*] Add 'Insert from URL' option to Video block [#41493] +## 1.78.0 + +- [*] Bump react-native-gesture-handler to version 2.3.2 [#41337] + ## 1.77.1 -- [***] Fixes a crash on iOS related to JSI and Reanimated [#41482] +- [***] Fix crash on iOS related to JSI and Reanimated [#41482] ## 1.77.0 @@ -23,7 +28,7 @@ For each user feature we should also add a importance categorization label to i ## 1.76.3 -- [***] Fixes a crash on iOS related to JSI and Reanimated [#41482] +- [***] Fix crash on iOS related to JSI and Reanimated [#41482] ## 1.76.2 @@ -38,28 +43,6 @@ For each user feature we should also add a importance categorization label to i ## 1.76.0 -## 1.78.0 - -- [*] Bump react-native-gesture-handler to version 2.3.2 [#41337] - -## 1.77.1 - -- [***] Fix crash on iOS related to JSI and Reanimated [#41482] - -## 1.77.0 - -- [*] [a11y] Improve text read by screen readers for BottomSheetSelectControl [#41036] -- [*] Add 'Insert from URL' option to Image block [#40334] - -## 1.76.1 - -- [*] BlockList - Add internal onLayout from CellRendererComponent to BlockListItemCell [#41105] -- [*] Fix Drag & Drop Chip positioning issue with RTL languages [#41053] -- [*] Add drag & drop help guide in Help & Support screen [#40961] -- [**] Fix drag mode not being enabled when long-pressing over Shortcode block [#41155] - -## 1.76.0 - - [**] [Buttons block] Fix Android-only issue related to displaying formatting buttons after closing the block settings [#40725] - [**] [Cover block] Improve color contrast between background and text [#40691] - [*] [Gallery block] Fix broken "Link To" settings and add "Image Size" settings [#40947]