From 21ff74f631dea2c22d9b804cb281d2fdd18ad7a1 Mon Sep 17 00:00:00 2001 From: Cameron Voell Date: Mon, 19 Aug 2019 11:43:38 -0700 Subject: [PATCH 01/31] Simplest implementation of fullscreen image preview using react-native-modal, modeled after BottomSheet usage --- .../block-library/src/image/edit.native.js | 16 ++++ packages/components/src/index.native.js | 1 + .../src/mobile/image-viewer/index.native.js | 85 +++++++++++++++++++ .../mobile/image-viewer/styles.native.scss | 22 +++++ 4 files changed, 124 insertions(+) create mode 100644 packages/components/src/mobile/image-viewer/index.native.js create mode 100644 packages/components/src/mobile/image-viewer/styles.native.scss diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index 3eb19a6bfa991b..ed16a106ff68c2 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -16,6 +16,7 @@ import { isEmpty } from 'lodash'; */ import { BottomSheet, + ImageViewer, Icon, Toolbar, ToolbarButton, @@ -53,6 +54,7 @@ class ImageEdit extends React.Component { this.state = { showSettings: false, isCaptionSelected: false, + showImageViewer: false, }; this.finishMediaUploadWithSuccess = this.finishMediaUploadWithSuccess.bind( this ); @@ -117,6 +119,7 @@ class ImageEdit extends React.Component { this.setState( { isCaptionSelected: false, + showImageViewer: true, } ); } @@ -211,6 +214,10 @@ class ImageEdit extends React.Component { this.setState( { showSettings: false } ); }; + const onImageViewerClose = () => { + this.setState( { showImageViewer: false } ); + }; + const getToolbarEditButton = ( open ) => ( @@ -256,6 +263,14 @@ class ImageEdit extends React.Component { ); + const getImageViewer = () => ( + + ); + if ( ! url ) { return ( @@ -279,6 +294,7 @@ class ImageEdit extends React.Component { > { getInspectorControls() } + { getImageViewer() } { getMediaOptions() } { ( ! this.state.isCaptionSelected ) && getToolbarEditButton( openMediaOptions ) diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index 4984cdde2fd36d..4a1a5b78e15379 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -22,6 +22,7 @@ export { default as withSpokenMessages } from './higher-order/with-spoken-messag // Mobile Components export { default as BottomSheet } from './mobile/bottom-sheet'; +export { default as ImageViewer } from './mobile/image-viewer'; export { default as HTMLTextInput } from './mobile/html-text-input'; export { default as KeyboardAvoidingView } from './mobile/keyboard-avoiding-view'; export { default as KeyboardAwareFlatList } from './mobile/keyboard-aware-flat-list'; diff --git a/packages/components/src/mobile/image-viewer/index.native.js b/packages/components/src/mobile/image-viewer/index.native.js new file mode 100644 index 00000000000000..2d6416f05e381c --- /dev/null +++ b/packages/components/src/mobile/image-viewer/index.native.js @@ -0,0 +1,85 @@ +/** + * External dependencies + */ +import { View, PanResponder, Dimensions, ImageBackground } from 'react-native'; +import Modal from 'react-native-modal'; + +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import styles from './styles.scss'; + +// Default Image ratio 4:3 +const IMAGE_ASPECT_RATIO = 4 / 3; + +class ImageViewer extends Component { + constructor() { + super( ...arguments ); + } + + render() { + const { + title = '', + isVisible, + style = {}, + url = '', + contentStyle = {}, + } = this.props; + + const self = this + + const panResponder = PanResponder.create( { + onMoveShouldSetPanResponder: ( evt, gestureState ) => { + // Activates swipe down over child Touchables if the swipe is long enough. + // With this we can adjust sensibility on the swipe vs tap gestures. + if ( gestureState.dy > 3 ) { + gestureState.dy = 0; + return true; + } + }, + } ); + + const imageContainerHeight = getWidth() / IMAGE_ASPECT_RATIO; + + return ( + + + + + + ); + } +} + +function getWidth() { + return Math.min( Dimensions.get( 'window' ).width, styles.background.maxWidth ); +} + +ImageViewer.getWidth = getWidth; + +export default ImageViewer; diff --git a/packages/components/src/mobile/image-viewer/styles.native.scss b/packages/components/src/mobile/image-viewer/styles.native.scss new file mode 100644 index 00000000000000..feab1828bab113 --- /dev/null +++ b/packages/components/src/mobile/image-viewer/styles.native.scss @@ -0,0 +1,22 @@ +//Image Viewer + +.bottomModal { + justify-content: center; + margin: 0; + align-items: center; +} + +.background { + background-color: $black; + border-top-right-radius: 8px; + border-top-left-radius: 8px; + width: 100%; + max-width: 512; + padding-bottom: 0; +} + +.content { + padding: 0 0 0 0; + justify-content: center; + +} From 73c7820f13b4d36d1d6b20aaf063b68514768f04 Mon Sep 17 00:00:00 2001 From: Cameron Voell Date: Tue, 20 Aug 2019 12:38:04 -0700 Subject: [PATCH 02/31] Added highlight border around image when image block is selected --- packages/block-library/src/image/edit.native.js | 4 +++- packages/block-library/src/image/styles.native.scss | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index ed16a106ff68c2..84f8efb1e6c772 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -285,6 +285,7 @@ class ImageEdit extends React.Component { } const imageContainerHeight = Dimensions.get( 'window' ).width / IMAGE_ASPECT_RATIO; + const getImageComponent = ( openMediaOptions, getMediaOptions ) => ( { const opacity = isUploadInProgress ? 0.3 : 1; const icon = this.getIcon( isUploadFailed ); + const imageBorderOnSelectedStyle = isSelected ? styles.imageBorder : ''; const iconContainer = ( @@ -337,7 +339,7 @@ class ImageEdit extends React.Component { accessibilityLabel={ alt } accessibilityHint={ __( 'Double tap and hold to edit' ) } accessibilityRole={ 'imagebutton' } - style={ { width: finalWidth, height: finalHeight, opacity } } + style={ [ imageBorderOnSelectedStyle, { width: finalWidth, height: finalHeight, opacity } ] } resizeMethod="scale" source={ { uri: url } } key={ url } diff --git a/packages/block-library/src/image/styles.native.scss b/packages/block-library/src/image/styles.native.scss index 81578bd734ba3e..03d01f46580417 100644 --- a/packages/block-library/src/image/styles.native.scss +++ b/packages/block-library/src/image/styles.native.scss @@ -6,6 +6,12 @@ background-color: $gray-lighten-30; } +.imageBorder { + border-color: $blue-medium; + border-width: 2px; + border-style: solid; +} + .uploadFailedText { color: #fff; font-size: 14; From 664ecd2c4a1cb99ade967f75f047c190ee3e3225 Mon Sep 17 00:00:00 2001 From: Cameron Voell Date: Tue, 20 Aug 2019 12:54:31 -0700 Subject: [PATCH 03/31] Fixed miss-copy of getImageViewer method. Image preview onImagePressed re-enabled --- packages/block-library/src/image/edit.native.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index 84f8efb1e6c772..8a7e3981bd8be3 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -265,8 +265,8 @@ class ImageEdit extends React.Component { const getImageViewer = () => ( ); From 231595e72b70f4740c2bd5b40de0476c5073c3e2 Mon Sep 17 00:00:00 2001 From: Cameron Voell Date: Thu, 22 Aug 2019 14:34:19 -0700 Subject: [PATCH 04/31] Image scales to correct size for fullscreen preview in portrait and landscape. Removed unnecessary code. --- .../src/mobile/image-viewer/index.native.js | 61 ++++++++++++++----- .../mobile/image-viewer/styles.native.scss | 14 +---- .../src/mobile/image-viewer/utils.js | 14 +++++ 3 files changed, 60 insertions(+), 29 deletions(-) create mode 100644 packages/components/src/mobile/image-viewer/utils.js diff --git a/packages/components/src/mobile/image-viewer/index.native.js b/packages/components/src/mobile/image-viewer/index.native.js index 2d6416f05e381c..ae92d198bd15a2 100644 --- a/packages/components/src/mobile/image-viewer/index.native.js +++ b/packages/components/src/mobile/image-viewer/index.native.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { View, PanResponder, Dimensions, ImageBackground } from 'react-native'; +import { View, PanResponder, Dimensions, ImageBackground, Image } from 'react-native'; import Modal from 'react-native-modal'; /** @@ -13,6 +13,7 @@ import { Component } from '@wordpress/element'; * Internal dependencies */ import styles from './styles.scss'; +import { calculateFullscreenImageSize } from './utils'; // Default Image ratio 4:3 const IMAGE_ASPECT_RATIO = 4 / 3; @@ -20,19 +21,54 @@ const IMAGE_ASPECT_RATIO = 4 / 3; class ImageViewer extends Component { constructor() { super( ...arguments ); + + this.state = { + width: undefined, + height: undefined, + }; + + this.onDimensionsChange = this.onDimensionsChange.bind( this ); + } + + componentDidMount() { + Dimensions.addEventListener("change", this.onDimensionsChange); + this.fetchImageSize(); + } + + componentDidUpdate( prevProps ) { + if ( this.props.url !== prevProps.url ) { + this.fetchImageSize(); + } + } + + componentWillUnmount() { + Dimensions.removeEventListener("change", this.onDimensionsChange); + } + + onDimensionsChange(dims) { + this.fetchImageSize(); + } + + fetchImageSize() { + this.container = Dimensions.get("window") + Image.getSize( this.props.url, ( width, height ) => { + this.image = { width, height }; + this.calculateSize(); + } ); + } + + calculateSize() { + const { width, height } = calculateFullscreenImageSize( this.image, this.container ); + this.setState( { width, height } ); } render() { const { title = '', isVisible, - style = {}, url = '', - contentStyle = {}, } = this.props; - const self = this - const panResponder = PanResponder.create( { onMoveShouldSetPanResponder: ( evt, gestureState ) => { // Activates swipe down over child Touchables if the swipe is long enough. @@ -44,12 +80,10 @@ class ImageViewer extends Component { }, } ); - const imageContainerHeight = getWidth() / IMAGE_ASPECT_RATIO; - return ( - + @@ -76,10 +111,4 @@ class ImageViewer extends Component { } } -function getWidth() { - return Math.min( Dimensions.get( 'window' ).width, styles.background.maxWidth ); -} - -ImageViewer.getWidth = getWidth; - export default ImageViewer; diff --git a/packages/components/src/mobile/image-viewer/styles.native.scss b/packages/components/src/mobile/image-viewer/styles.native.scss index feab1828bab113..c66b5500fa09fb 100644 --- a/packages/components/src/mobile/image-viewer/styles.native.scss +++ b/packages/components/src/mobile/image-viewer/styles.native.scss @@ -1,22 +1,10 @@ //Image Viewer -.bottomModal { +.modal { justify-content: center; - margin: 0; align-items: center; } -.background { - background-color: $black; - border-top-right-radius: 8px; - border-top-left-radius: 8px; - width: 100%; - max-width: 512; - padding-bottom: 0; -} - .content { - padding: 0 0 0 0; justify-content: center; - } diff --git a/packages/components/src/mobile/image-viewer/utils.js b/packages/components/src/mobile/image-viewer/utils.js new file mode 100644 index 00000000000000..a473625a0bfd11 --- /dev/null +++ b/packages/components/src/mobile/image-viewer/utils.js @@ -0,0 +1,14 @@ + +export function calculateFullscreenImageSize( image, container ) { + const screenRatio = container.width / container.height; + const imageRatio = image.width / image.height; + let width = container.width; + let height = container.height; + const shouldUseContainerHeightForImage = imageRatio < screenRatio + if (shouldUseContainerHeightForImage) { + width = container.height * imageRatio; + } else { + height = container.width / imageRatio; + } + return { width, height }; +} \ No newline at end of file From 3b3f645ebd9463b76b079ae07d65ddf0a3c0ee56 Mon Sep 17 00:00:00 2001 From: Cameron Voell Date: Mon, 26 Aug 2019 13:32:18 -0700 Subject: [PATCH 05/31] Fixed lint errors. Small refactor so we can utilize dimension parameter of onDimensionsChanged --- .../src/mobile/image-viewer/index.native.js | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/packages/components/src/mobile/image-viewer/index.native.js b/packages/components/src/mobile/image-viewer/index.native.js index ae92d198bd15a2..d7758b5ca7c58f 100644 --- a/packages/components/src/mobile/image-viewer/index.native.js +++ b/packages/components/src/mobile/image-viewer/index.native.js @@ -15,9 +15,6 @@ import { Component } from '@wordpress/element'; import styles from './styles.scss'; import { calculateFullscreenImageSize } from './utils'; -// Default Image ratio 4:3 -const IMAGE_ASPECT_RATIO = 4 / 3; - class ImageViewer extends Component { constructor() { super( ...arguments ); @@ -29,28 +26,28 @@ class ImageViewer extends Component { this.onDimensionsChange = this.onDimensionsChange.bind( this ); } - + componentDidMount() { - Dimensions.addEventListener("change", this.onDimensionsChange); - this.fetchImageSize(); + Dimensions.addEventListener( 'change', this.onDimensionsChange ); + this.fetchImageSize( Dimensions.get( 'window' ) ); } componentDidUpdate( prevProps ) { if ( this.props.url !== prevProps.url ) { - this.fetchImageSize(); + this.fetchImageSize( Dimensions.get( 'window' ) ); } } - componentWillUnmount() { - Dimensions.removeEventListener("change", this.onDimensionsChange); + componentWillUnmount() { + Dimensions.removeEventListener( 'change', this.onDimensionsChange ); } - onDimensionsChange(dims) { - this.fetchImageSize(); + onDimensionsChange( dimensions ) { + this.fetchImageSize( dimensions.window ); } - fetchImageSize() { - this.container = Dimensions.get("window") + fetchImageSize( dimensions ) { + this.container = dimensions; Image.getSize( this.props.url, ( width, height ) => { this.image = { width, height }; this.calculateSize(); @@ -64,11 +61,10 @@ class ImageViewer extends Component { render() { const { - title = '', isVisible, - url = '', + url = '', } = this.props; - + const panResponder = PanResponder.create( { onMoveShouldSetPanResponder: ( evt, gestureState ) => { // Activates swipe down over child Touchables if the swipe is long enough. @@ -84,25 +80,25 @@ class ImageViewer extends Component { - From ec59c5616c948a11fd939c86e4efd5fbba48463f Mon Sep 17 00:00:00 2001 From: Cameron Voell Date: Mon, 26 Aug 2019 13:47:26 -0700 Subject: [PATCH 06/31] Refactored, removing extra calculateSize method, image variable, and removing utils class since it is not being used anywhere else. --- .../src/mobile/image-viewer/index.native.js | 21 ++++++++++++------- .../src/mobile/image-viewer/utils.js | 14 ------------- 2 files changed, 14 insertions(+), 21 deletions(-) delete mode 100644 packages/components/src/mobile/image-viewer/utils.js diff --git a/packages/components/src/mobile/image-viewer/index.native.js b/packages/components/src/mobile/image-viewer/index.native.js index d7758b5ca7c58f..a1a59013b7cc1d 100644 --- a/packages/components/src/mobile/image-viewer/index.native.js +++ b/packages/components/src/mobile/image-viewer/index.native.js @@ -13,7 +13,6 @@ import { Component } from '@wordpress/element'; * Internal dependencies */ import styles from './styles.scss'; -import { calculateFullscreenImageSize } from './utils'; class ImageViewer extends Component { constructor() { @@ -47,16 +46,24 @@ class ImageViewer extends Component { } fetchImageSize( dimensions ) { - this.container = dimensions; Image.getSize( this.props.url, ( width, height ) => { - this.image = { width, height }; - this.calculateSize(); + const { finalWidth, finalHeight } = this.calculateFullscreenImageSize( width, height, dimensions ); + this.setState( { width: finalWidth, height: finalHeight } ); } ); } - calculateSize() { - const { width, height } = calculateFullscreenImageSize( this.image, this.container ); - this.setState( { width, height } ); + calculateFullscreenImageSize( imageWidth, imageHeight, container ) { + const imageRatio = imageWidth / imageHeight; + const screenRatio = container.width / container.height; + let finalWidth = container.width; + let finalHeight = container.height; + const shouldUseContainerHeightForImage = imageRatio < screenRatio; + if ( shouldUseContainerHeightForImage ) { + finalWidth = container.height * imageRatio; + } else { + finalHeight = container.width / imageRatio; + } + return { finalWidth, finalHeight }; } render() { diff --git a/packages/components/src/mobile/image-viewer/utils.js b/packages/components/src/mobile/image-viewer/utils.js deleted file mode 100644 index a473625a0bfd11..00000000000000 --- a/packages/components/src/mobile/image-viewer/utils.js +++ /dev/null @@ -1,14 +0,0 @@ - -export function calculateFullscreenImageSize( image, container ) { - const screenRatio = container.width / container.height; - const imageRatio = image.width / image.height; - let width = container.width; - let height = container.height; - const shouldUseContainerHeightForImage = imageRatio < screenRatio - if (shouldUseContainerHeightForImage) { - width = container.height * imageRatio; - } else { - height = container.width / imageRatio; - } - return { width, height }; -} \ No newline at end of file From 9d3bbab92d3c0ca4fdb97a6f6cfacac94eb0b4bd Mon Sep 17 00:00:00 2001 From: Cameron Voell Date: Mon, 26 Aug 2019 15:09:02 -0700 Subject: [PATCH 07/31] Removed custom PanResponder setup. Does not seem to affect ImageViewer, perhaps becauser we have no children tap events we need to worry about. Fixed animationOutTiming constant. --- .../src/mobile/image-viewer/index.native.js | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/packages/components/src/mobile/image-viewer/index.native.js b/packages/components/src/mobile/image-viewer/index.native.js index a1a59013b7cc1d..a7e8f2eeedd25f 100644 --- a/packages/components/src/mobile/image-viewer/index.native.js +++ b/packages/components/src/mobile/image-viewer/index.native.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { View, PanResponder, Dimensions, ImageBackground, Image } from 'react-native'; +import { View, Dimensions, ImageBackground, Image } from 'react-native'; import Modal from 'react-native-modal'; /** @@ -72,17 +72,6 @@ class ImageViewer extends Component { url = '', } = this.props; - const panResponder = PanResponder.create( { - onMoveShouldSetPanResponder: ( evt, gestureState ) => { - // Activates swipe down over child Touchables if the swipe is long enough. - // With this we can adjust sensibility on the swipe vs tap gestures. - if ( gestureState.dy > 3 ) { - gestureState.dy = 0; - return true; - } - }, - } ); - return ( From 635108e9d29b907aeb7aa82a103473ad5601d64c Mon Sep 17 00:00:00 2001 From: etoledom Date: Wed, 28 Aug 2019 10:31:39 +0200 Subject: [PATCH 08/31] [RNMobile] Native mobile release v1.11.0 (#17181) * [RNMobile] Fix crash when adding separator * Build: remove global install of latest npm since we want to use the paired node/npm version (#17134) * Build: remove global install of latest npm since we want to use the paired node/npm version * Also update travis to remove --latest-npm flag * [RNMobile] Try dark mode (iOS) (#17067) * Adding dark mode component implemented on list and list block * Adding DarkMode handling to RichText, ToolBar and SafeArea * Mobile: Using DarkMode as HOC * iOS DarkMode: Modified colors on block list and block container * iOS DarkMode: Improved Header Toolbar colors * iOS DarkMode: Removing background from buttons * iOS DarkMode warning and unsupported * iOS DarkMode: MediaPlaceholder * iOS DarkMode: BottomSheets * iOS DarkMode: Inserter * iOS DarkMode: DefaultBlockAppender * iOS DarkMode: PostTite * Update hardcoded colors with variables * iOS DarkMode: Fix bottom-sheet cell value color * iOS DarkMode: More - PageBreak - Add Block Here * iOS DarkMode: Better text color * iOS Darkmode: Code block * iOS DarkMode: HTML View * iOS DarkMode: Improve colors on SafeArea * Fix toolbar not avoiding keyboard regression * Fix native unit tests * Fix gutenberg-mobile unit tests * Adding RNDarkMode mocks * RNMobile: Fix crash when viewing HTML on iOS * [RNMobile] Remove toolbar from html view * [RNMobile] Fix MaxListenersExceededWarning caused by dark-mode event emitter (#17186) * Fix MaxListenersExceededWarning caused by dark-mode event emitter * Checking for setMaxListeners trying to avoid CI error * Adding remove listener to DarkMode HOC * DarkMode: Binding this.onModeChanged to `this` * DarkMode: Adding conditional needed to pass UI Tests on CI * Fix focus title on new posts regression (#17180) * BottomSheet: Setting DashIcon color directly when theme is default (light) (#17193) --- .../components/block-list/block.native.scss | 2 - .../src/components/block-list/index.native.js | 16 +++--- .../components/block-list/style.native.scss | 18 ++++++- .../default-block-appender/style.native.scss | 1 - .../src/components/inserter/index.native.js | 12 +++-- .../src/components/inserter/menu.native.js | 14 +++-- .../src/components/inserter/style.native.scss | 17 ++++++ .../media-placeholder/index.native.js | 12 +++-- .../media-placeholder/styles.native.scss | 8 +++ .../src/components/warning/index.native.js | 18 ++++--- .../src/components/warning/style.native.scss | 12 +++++ .../block-library/src/code/edit.native.js | 13 +++-- .../block-library/src/code/theme.native.scss | 11 ++++ .../block-library/src/image/edit.native.js | 9 +++- .../src/image/styles.native.scss | 4 ++ .../block-library/src/missing/edit.native.js | 19 ++++--- .../src/missing/style.native.scss | 12 +++++ .../block-library/src/more/edit.native.js | 11 ++-- .../block-library/src/more/editor.native.scss | 12 ++++- .../block-library/src/nextpage/edit.native.js | 11 ++-- .../src/nextpage/editor.native.scss | 13 ++++- packages/block-library/src/separator/edit.js | 28 ++++------ .../src/separator/separator-settings.js | 26 +++++++++ .../separator/separator-settings.native.js | 3 ++ .../block-library/src/video/edit.native.js | 9 +++- .../block-library/src/video/style.native.scss | 4 ++ .../components/src/button/index.native.js | 2 +- packages/components/src/index.native.js | 1 + .../src/mobile/bottom-sheet/cell.native.js | 29 +++++++--- .../src/mobile/bottom-sheet/index.native.js | 20 ++++--- .../mobile/bottom-sheet/styles.native.scss | 24 +++++++++ .../src/mobile/dark-mode/index.native.js | 53 +++++++++++++++++++ .../mobile/html-text-input/index.native.js | 8 ++- .../html-text-input/style-common.native.scss | 10 +++- .../mobile/html-text-input/style.android.scss | 2 - .../src/mobile/html-text-input/style.ios.scss | 6 ++- .../src/primitives/svg/style.native.scss | 14 ++++- .../components/src/toolbar/style.native.scss | 4 ++ .../src/toolbar/toolbar-container.native.js | 5 +- .../header/header-toolbar/index.native.js | 6 ++- .../header/header-toolbar/style.native.scss | 5 ++ .../src/components/layout/index.native.js | 28 +++++----- .../src/components/layout/style.native.scss | 13 +++++ .../components/visual-editor/index.native.js | 8 +-- .../visual-editor/style.native.scss | 4 ++ packages/edit-post/src/editor.native.js | 21 ++++++++ .../src/components/provider/index.native.js | 17 ------ .../src/link/test/modal.native.js | 8 +-- .../rich-text/src/component/index.native.js | 15 ++++-- .../rich-text/src/component/style.native.scss | 14 ++++- .../__mocks__/react-native-dark-mode/index.js | 0 test/native/__mocks__/styleMock.js | 11 +++- test/native/setup.js | 9 ++++ 53 files changed, 507 insertions(+), 145 deletions(-) create mode 100644 packages/block-library/src/separator/separator-settings.js create mode 100644 packages/block-library/src/separator/separator-settings.native.js create mode 100644 packages/components/src/mobile/dark-mode/index.native.js create mode 100644 test/native/__mocks__/react-native-dark-mode/index.js diff --git a/packages/block-editor/src/components/block-list/block.native.scss b/packages/block-editor/src/components/block-list/block.native.scss index b997cddc723073..a7dbae3b46516b 100644 --- a/packages/block-editor/src/components/block-list/block.native.scss +++ b/packages/block-editor/src/components/block-list/block.native.scss @@ -3,7 +3,6 @@ } .blockContainer { - background-color: $white; padding-left: 16px; padding-right: 16px; padding-top: 12px; @@ -11,7 +10,6 @@ } .blockContainerFocused { - background-color: $white; padding-left: 16px; padding-right: 16px; padding-top: 12px; 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 facef0cc5e01ca..10a5cfcbcf1bae 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -12,7 +12,7 @@ import { __ } from '@wordpress/i18n'; import { withDispatch, withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import { createBlock, isUnmodifiedDefaultBlock } from '@wordpress/blocks'; -import { KeyboardAwareFlatList, ReadableContentView } from '@wordpress/components'; +import { KeyboardAwareFlatList, ReadableContentView, useStyle, withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -103,7 +103,7 @@ export class BlockList extends Component { innerRef={ this.scrollViewInnerRef } extraScrollHeight={ innerToolbarHeight + 10 } keyboardShouldPersistTaps="always" - style={ styles.list } + style={ useStyle( styles.list, styles.listDark, this.context ) } data={ this.props.blockClientIds } extraData={ [ this.props.isFullyBordered ] } keyExtractor={ identity } @@ -126,6 +126,7 @@ export class BlockList extends Component { } renderItem( { item: clientId, index } ) { + const blockHolderFocusedStyle = useStyle( styles.blockHolderFocused, styles.blockHolderFocusedDark, this.props.theme ); const { shouldShowBlockAtIndex, shouldShowInsertionPoint } = this.props; return ( @@ -138,18 +139,20 @@ export class BlockList extends Component { rootClientId={ this.props.rootClientId } onCaretVerticalPositionChange={ this.onCaretVerticalPositionChange } borderStyle={ this.blockHolderBorderStyle() } - focusedBorderColor={ styles.blockHolderFocused.borderColor } + focusedBorderColor={ blockHolderFocusedStyle } /> ) } ); } renderAddBlockSeparator() { + const lineStyle = useStyle( styles.lineStyleAddHere, styles.lineStyleAddHereDark, this.props.theme ); + const labelStyle = useStyle( styles.labelStyleAddHere, styles.labelStyleAddHereDark, this.props.theme ); return ( - - { __( 'ADD BLOCK HERE' ) } - + + { __( 'ADD BLOCK HERE' ) } + ); } @@ -228,5 +231,6 @@ export default compose( [ replaceBlock, }; } ), + withTheme, ] )( BlockList ); diff --git a/packages/block-editor/src/components/block-list/style.native.scss b/packages/block-editor/src/components/block-list/style.native.scss index b6961600b95c67..e1f8e96abc948f 100644 --- a/packages/block-editor/src/components/block-list/style.native.scss +++ b/packages/block-editor/src/components/block-list/style.native.scss @@ -8,6 +8,10 @@ flex: 1; } +.listDark { + background: #1c1c1e; +} + .switch { flex-direction: row; justify-content: flex-start; @@ -26,6 +30,10 @@ height: 2px; } +.lineStyleAddHereDark { + background-color: $gray-50; +} + .labelStyleAddHere { flex: 1; text-align: center; @@ -34,9 +42,12 @@ font-weight: bold; } +.labelStyleAddHereDark { + color: $gray-20; +} + .containerStyleAddHere { flex-direction: row; - background-color: $white; } .blockHolderSemiBordered { @@ -54,7 +65,6 @@ } .blockContainerFocused { - background-color: $white; padding-left: 16; padding-right: 16; padding-top: 12; @@ -65,6 +75,10 @@ border-color: $gray-lighten-30; } +.blockHolderFocusedDark { + border-color: $gray-70; +} + .blockListFooter { height: 80px; } diff --git a/packages/block-editor/src/components/default-block-appender/style.native.scss b/packages/block-editor/src/components/default-block-appender/style.native.scss index 5193611fa45d58..9e2ffd2f293b01 100644 --- a/packages/block-editor/src/components/default-block-appender/style.native.scss +++ b/packages/block-editor/src/components/default-block-appender/style.native.scss @@ -5,7 +5,6 @@ } .blockContainer { - background-color: $white; padding-top: 0; padding-left: 16px; padding-right: 16px; diff --git a/packages/block-editor/src/components/inserter/index.native.js b/packages/block-editor/src/components/inserter/index.native.js index b20eb8387992d6..1a757a65a502b2 100644 --- a/packages/block-editor/src/components/inserter/index.native.js +++ b/packages/block-editor/src/components/inserter/index.native.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { Dropdown, ToolbarButton, Dashicon } from '@wordpress/components'; +import { Dropdown, ToolbarButton, Dashicon, withTheme, useStyle } from '@wordpress/components'; import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; @@ -14,10 +14,10 @@ import { getUnregisteredTypeHandlerName } from '@wordpress/blocks'; import styles from './style.scss'; import InserterMenu from './menu'; -const defaultRenderToggle = ( { onToggle, disabled } ) => ( +const defaultRenderToggle = ( { onToggle, disabled, style } ) => ( ) } + icon={ ( ) } onClick={ onToggle } extraProps={ { hint: __( 'Double tap to add a block' ) } } isDisabled={ disabled } @@ -56,9 +56,10 @@ class Inserter extends Component { const { disabled, renderToggle = defaultRenderToggle, + theme, } = this.props; - - return renderToggle( { onToggle, isOpen, disabled } ); + const style = useStyle( styles.addBlockButton, styles.addBlockButtonDark, theme ); + return renderToggle( { onToggle, isOpen, disabled, style } ); } /** @@ -118,4 +119,5 @@ export default compose( [ items: inserterItems.filter( ( { name } ) => name !== getUnregisteredTypeHandlerName() ), }; } ), + withTheme, ] )( Inserter ); diff --git a/packages/block-editor/src/components/inserter/menu.native.js b/packages/block-editor/src/components/inserter/menu.native.js index 7870f41954afe3..3b0e27a156ddb8 100644 --- a/packages/block-editor/src/components/inserter/menu.native.js +++ b/packages/block-editor/src/components/inserter/menu.native.js @@ -14,7 +14,7 @@ import { } from '@wordpress/blocks'; import { withDispatch, withSelect } from '@wordpress/data'; import { withInstanceId, compose } from '@wordpress/compose'; -import { BottomSheet, Icon } from '@wordpress/components'; +import { BottomSheet, Icon, withTheme, useStyle } from '@wordpress/components'; /** * Internal dependencies @@ -63,6 +63,9 @@ export class InserterMenu extends Component { render() { const numberOfColumns = this.calculateNumberOfColumns(); const bottomPadding = styles.contentBottomPadding; + const modalIconWrapperStyle = useStyle( styles.modalIconWrapper, styles.modalIconWrapperDark, this.props.theme ); + const modalIconStyle = useStyle( styles.modalIcon, styles.modalIconDark, this.props.theme ); + const modalItemLabelStyle = useStyle( styles.modalItemLabel, styles.modalItemLabelDark, this.props.theme ); return ( this.props.onSelect( item ) }> - - - + + + - { item.title } + { item.title } } @@ -213,4 +216,5 @@ export default compose( }; } ), withInstanceId, + withTheme, )( InserterMenu ); diff --git a/packages/block-editor/src/components/inserter/style.native.scss b/packages/block-editor/src/components/inserter/style.native.scss index e10b685dda406c..899b3f676827db 100644 --- a/packages/block-editor/src/components/inserter/style.native.scss +++ b/packages/block-editor/src/components/inserter/style.native.scss @@ -37,6 +37,10 @@ align-items: center; } +.modalIconWrapperDark { + background-color: rgba($white, 0.07); +} + .modalIcon { width: 32px; height: 32px; @@ -45,6 +49,10 @@ fill: $gray-dark; } +.modalIconDark { + fill: $white; +} + .modalItemLabel { background-color: transparent; padding-left: 2; @@ -56,9 +64,18 @@ color: $gray-dark; } +.modalItemLabelDark { + color: $white; +} + .addBlockButton { color: $blue-wordpress; border: 2px; border-radius: 10px; border-color: $blue-wordpress; } + +.addBlockButtonDark { + color: $blue-30; + border-color: $blue-30; +} diff --git a/packages/block-editor/src/components/media-placeholder/index.native.js b/packages/block-editor/src/components/media-placeholder/index.native.js index a19a28588200c9..e04361b71f3307 100644 --- a/packages/block-editor/src/components/media-placeholder/index.native.js +++ b/packages/block-editor/src/components/media-placeholder/index.native.js @@ -8,6 +8,7 @@ import { View, Text, TouchableWithoutFeedback } from 'react-native'; */ import { __, sprintf } from '@wordpress/i18n'; import { MediaUpload, MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO } from '@wordpress/block-editor'; +import { withTheme, useStyle } from '@wordpress/components'; /** * Internal dependencies @@ -15,7 +16,7 @@ import { MediaUpload, MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO } from '@wordpress/bloc import styles from './styles.scss'; function MediaPlaceholder( props ) { - const { mediaType, labels = {}, icon, onSelectURL } = props; + const { mediaType, labels = {}, icon, onSelectURL, theme } = props; const isImage = MEDIA_TYPE_IMAGE === mediaType; const isVideo = MEDIA_TYPE_VIDEO === mediaType; @@ -46,6 +47,9 @@ function MediaPlaceholder( props ) { accessibilityHint = __( 'Double tap to select a video' ); } + const emptyStateContainerStyle = useStyle( styles.emptyStateContainer, styles.emptyStateContainerDark, theme ); + const emptyStateTitleStyle = useStyle( styles.emptyStateTitle, styles.emptyStateTitleDark, theme ); + return ( - + { getMediaOptions() } { icon } - + { placeholderTitle } @@ -83,4 +87,4 @@ function MediaPlaceholder( props ) { ); } -export default MediaPlaceholder; +export default withTheme( MediaPlaceholder ); diff --git a/packages/block-editor/src/components/media-placeholder/styles.native.scss b/packages/block-editor/src/components/media-placeholder/styles.native.scss index f76be4b8f36a42..7a192f078e2090 100644 --- a/packages/block-editor/src/components/media-placeholder/styles.native.scss +++ b/packages/block-editor/src/components/media-placeholder/styles.native.scss @@ -11,6 +11,10 @@ padding-bottom: 12; } +.emptyStateContainerDark { + background-color: $background-dark-secondary; +} + .emptyStateTitle { text-align: center; margin-top: 8; @@ -19,6 +23,10 @@ color: #2e4453; } +.emptyStateTitleDark { + color: $white; +} + .emptyStateDescription { text-align: center; color: $blue-wordpress; diff --git a/packages/block-editor/src/components/warning/index.native.js b/packages/block-editor/src/components/warning/index.native.js index 7d0cced3408cca..071fd30a0738a3 100644 --- a/packages/block-editor/src/components/warning/index.native.js +++ b/packages/block-editor/src/components/warning/index.native.js @@ -6,7 +6,7 @@ import { View, Text } from 'react-native'; /** * WordPress dependencies */ -import { Icon } from '@wordpress/components'; +import { Icon, withTheme, useStyle } from '@wordpress/components'; import { normalizeIconObject } from '@wordpress/blocks'; /** @@ -14,29 +14,33 @@ import { normalizeIconObject } from '@wordpress/blocks'; */ import styles from './style.scss'; -function Warning( { title, message, icon, iconClass, ...viewProps } ) { +function Warning( { title, message, icon, iconClass, theme, ...viewProps } ) { icon = icon && normalizeIconObject( icon ); + const internalIconClass = 'warning-icon' + '-' + theme; + const titleStyle = useStyle( styles.title, styles.titleDark, theme ); + const messageStyle = useStyle( styles.message, styles.messageDark, theme ); + return ( { icon && ( ) } { title && ( - { title } + { title } ) } { message && ( - { message } + { message } ) } ); } -export default Warning; +export default withTheme( Warning ); diff --git a/packages/block-editor/src/components/warning/style.native.scss b/packages/block-editor/src/components/warning/style.native.scss index a96045ada1fd55..2bfc52da454d25 100644 --- a/packages/block-editor/src/components/warning/style.native.scss +++ b/packages/block-editor/src/components/warning/style.native.scss @@ -10,6 +10,10 @@ justify-content: center; } +.containerDark { + background-color: $background-dark-secondary; +} + .icon { width: 24px; height: 24px; @@ -24,8 +28,16 @@ color: $gray-dark; } +.titleDark { + color: $white; +} + .message { text-align: center; color: $gray-text-min; font-size: 12; } + +.messageDark { + color: $white; +} diff --git a/packages/block-library/src/code/edit.native.js b/packages/block-library/src/code/edit.native.js index 3f021790c2a403..0363fcfebc61d0 100644 --- a/packages/block-library/src/code/edit.native.js +++ b/packages/block-library/src/code/edit.native.js @@ -6,12 +6,13 @@ import { View } from 'react-native'; /** * WordPress dependencies */ +import { PlainText } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; +import { withTheme, useStyle } from '@wordpress/components'; /** * Internal dependencies */ -import { PlainText } from '@wordpress/block-editor'; import { escape, unescape } from './utils'; /** @@ -21,14 +22,16 @@ import styles from './theme.scss'; // Note: styling is applied directly to the (nested) PlainText component. Web-side components // apply it to the container 'div' but we don't have a proper proposal for cascading styling yet. -export default function CodeEdit( props ) { - const { attributes, setAttributes, style, onFocus, onBlur } = props; +export function CodeEdit( props ) { + const { attributes, setAttributes, style, onFocus, onBlur, theme } = props; + const codeStyle = useStyle( styles.blockCode, styles.blockCodeDark, theme ); + const placeholderStyle = useStyle( styles.placeholder, styles.placeholderDark, theme ); return ( setAttributes( { content: escape( content ) } ) } @@ -38,8 +41,10 @@ export default function CodeEdit( props ) { onFocus={ onFocus } onBlur={ onBlur } fontFamily={ ( styles.blockCode.fontFamily ) } + placeholderTextColor={ placeholderStyle.color } /> </View> ); } +export default withTheme( CodeEdit ); diff --git a/packages/block-library/src/code/theme.native.scss b/packages/block-library/src/code/theme.native.scss index 668b9f92dd1f53..40a4ba9bfcbfdf 100644 --- a/packages/block-library/src/code/theme.native.scss +++ b/packages/block-library/src/code/theme.native.scss @@ -4,3 +4,14 @@ font-family: $default-monospace-font; } +.blockCodeDark { + color: $white; +} + +.placeholder { + color: $gray; +} + +.placeholderDark { + color: $gray-50; +} diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index 3eb19a6bfa991b..a21d4bb72830f5 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -19,7 +19,10 @@ import { Icon, Toolbar, ToolbarButton, + withTheme, + useStyle, } from '@wordpress/components'; + import { Caption, MediaPlaceholder, @@ -192,11 +195,13 @@ class ImageEdit extends React.Component { } getIcon( isRetryIcon ) { + const iconStyle = useStyle( styles.icon, styles.iconDark, this.props.theme ); + if ( isRetryIcon ) { return <Icon icon={ SvgIconRetry } { ...styles.iconRetry } />; } - return <Icon icon={ SvgIcon } { ...styles.icon } />; + return <Icon icon={ SvgIcon } { ...iconStyle } />; } render() { @@ -368,4 +373,4 @@ class ImageEdit extends React.Component { } } -export default ImageEdit; +export default withTheme( ImageEdit ); diff --git a/packages/block-library/src/image/styles.native.scss b/packages/block-library/src/image/styles.native.scss index 81578bd734ba3e..aaafc3ae5457c8 100644 --- a/packages/block-library/src/image/styles.native.scss +++ b/packages/block-library/src/image/styles.native.scss @@ -45,3 +45,7 @@ width: 100%; height: 100%; } + +.iconDark { + fill: $white; +} diff --git a/packages/block-library/src/missing/edit.native.js b/packages/block-library/src/missing/edit.native.js index 838c64d88b56c4..c5a604144b7e7b 100644 --- a/packages/block-library/src/missing/edit.native.js +++ b/packages/block-library/src/missing/edit.native.js @@ -6,7 +6,7 @@ import { View, Text } from 'react-native'; /** * WordPress dependencies */ -import { Icon } from '@wordpress/components'; +import { Icon, withTheme, useStyle } from '@wordpress/components'; import { coreBlocks } from '@wordpress/block-library'; import { normalizeIconObject } from '@wordpress/blocks'; import { Component } from '@wordpress/element'; @@ -17,18 +17,25 @@ import { __ } from '@wordpress/i18n'; */ import styles from './style.scss'; -export default class UnsupportedBlockEdit extends Component { +export class UnsupportedBlockEdit extends Component { render() { const { originalName } = this.props.attributes; + const theme = this.props.theme; const blockType = coreBlocks[ originalName ]; + const title = blockType ? blockType.settings.title : __( 'Unsupported' ); - const icon = blockType ? normalizeIconObject( blockType.settings.icon ) : 'admin-plugins'; + const titleStyle = useStyle( styles.unsupportedBlockMessage, styles.unsupportedBlockMessageDark, theme ); + const icon = blockType ? normalizeIconObject( blockType.settings.icon ) : 'admin-plugins'; + const iconStyle = useStyle( styles.unsupportedBlockIcon, styles.unsupportedBlockIconDark, theme ); + const iconClassName = 'unsupported-icon' + '-' + theme; return ( - <View style={ styles.unsupportedBlock }> - <Icon className="unsupported-icon" icon={ icon && icon.src ? icon.src : icon } /> - <Text style={ styles.unsupportedBlockMessage }>{ title }</Text> + <View style={ useStyle( styles.unsupportedBlock, styles.unsupportedBlockDark, theme ) }> + <Icon className={ iconClassName } icon={ icon && icon.src ? icon.src : icon } color={ iconStyle.color } /> + <Text style={ titleStyle }>{ title }</Text> </View> ); } } + +export default withTheme( UnsupportedBlockEdit ); diff --git a/packages/block-library/src/missing/style.native.scss b/packages/block-library/src/missing/style.native.scss index 95a86122d70983..c3f9c3a935c630 100644 --- a/packages/block-library/src/missing/style.native.scss +++ b/packages/block-library/src/missing/style.native.scss @@ -10,13 +10,25 @@ justify-content: center; } +.unsupportedBlockDark { + background-color: $background-dark-secondary; +} + .unsupportedBlockIcon { color: $gray-dark; } +.unsupportedBlockIconDark { + color: $white; +} + .unsupportedBlockMessage { margin-top: 2; text-align: center; color: $gray-dark; font-size: 14; } + +.unsupportedBlockMessageDark { + color: $white; +} diff --git a/packages/block-library/src/more/edit.native.js b/packages/block-library/src/more/edit.native.js index 8b369284cb5962..e177213a7f1f6f 100644 --- a/packages/block-library/src/more/edit.native.js +++ b/packages/block-library/src/more/edit.native.js @@ -9,13 +9,14 @@ import Hr from 'react-native-hr'; */ import { __ } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; +import { withTheme, useStyle } from '@wordpress/components'; /** * Internal dependencies */ import styles from './editor.scss'; -export default class MoreEdit extends Component { +export class MoreEdit extends Component { constructor() { super( ...arguments ); @@ -28,6 +29,8 @@ export default class MoreEdit extends Component { const { customText } = this.props.attributes; const { defaultText } = this.state; const content = customText || defaultText; + const textStyle = useStyle( styles.moreText, styles.moreTextDark, this.props.theme ); + const lineStyle = useStyle( styles.moreLine, styles.moreLineDark, this.props.theme ); return ( <View> @@ -35,10 +38,12 @@ export default class MoreEdit extends Component { text={ content } marginLeft={ 0 } marginRight={ 0 } - textStyle={ styles[ 'block-library-more__text' ] } - lineStyle={ styles[ 'block-library-more__line' ] } + textStyle={ textStyle } + lineStyle={ lineStyle } /> </View> ); } } + +export default withTheme( MoreEdit ); diff --git a/packages/block-library/src/more/editor.native.scss b/packages/block-library/src/more/editor.native.scss index eb4a1d60d9431f..b0dece50736e6b 100644 --- a/packages/block-library/src/more/editor.native.scss +++ b/packages/block-library/src/more/editor.native.scss @@ -1,13 +1,21 @@ // @format -.block-library-more__line { +.moreLine { background-color: $gray-lighten-20; height: 2; } -.block-library-more__text { +.moreLineDark { + background-color: $gray-50; +} + +.moreText { color: $gray; text-decoration-style: solid; text-transform: uppercase; } +.moreTextDark { + color: $gray-20; +} + diff --git a/packages/block-library/src/nextpage/edit.native.js b/packages/block-library/src/nextpage/edit.native.js index e3aa69b15e5e41..be9ad283576401 100644 --- a/packages/block-library/src/nextpage/edit.native.js +++ b/packages/block-library/src/nextpage/edit.native.js @@ -8,16 +8,19 @@ import Hr from 'react-native-hr'; * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; +import { withTheme, useStyle } from '@wordpress/components'; /** * Internal dependencies */ import styles from './editor.scss'; -export default function NextPageEdit( { attributes, isSelected, onFocus } ) { +export function NextPageEdit( { attributes, isSelected, onFocus, theme } ) { const { customText = __( 'Page break' ) } = attributes; const accessibilityTitle = attributes.customText || ''; const accessibilityState = isSelected ? [ 'selected' ] : []; + const textStyle = useStyle( styles.nextpageText, styles.nextpageTextDark, theme ); + const lineStyle = useStyle( styles.nextpageLine, styles.nextpageLineDark, theme ); return ( <View @@ -35,8 +38,10 @@ export default function NextPageEdit( { attributes, isSelected, onFocus } ) { <Hr text={ customText } marginLeft={ 0 } marginRight={ 0 } - textStyle={ styles[ 'block-library-nextpage__text' ] } - lineStyle={ styles[ 'block-library-nextpage__line' ] } /> + textStyle={ textStyle } + lineStyle={ lineStyle } /> </View> ); } + +export default withTheme( NextPageEdit ); diff --git a/packages/block-library/src/nextpage/editor.native.scss b/packages/block-library/src/nextpage/editor.native.scss index 869851fdd37c63..01ed97ac577473 100644 --- a/packages/block-library/src/nextpage/editor.native.scss +++ b/packages/block-library/src/nextpage/editor.native.scss @@ -1,12 +1,21 @@ // @format -.block-library-nextpage__line { +.nextpageLine { background-color: $gray-lighten-20; height: 2; } -.block-library-nextpage__text { +.nextpageLineDark { + background-color: $gray-50; +} + +.nextpageText { color: $gray; text-decoration-style: solid; text-transform: uppercase; } + +.nextpageTextDark { + color: $gray-20; +} + diff --git a/packages/block-library/src/separator/edit.js b/packages/block-library/src/separator/edit.js index 050bd7423fb665..30dd0e85d71549 100644 --- a/packages/block-library/src/separator/edit.js +++ b/packages/block-library/src/separator/edit.js @@ -6,13 +6,12 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; import { HorizontalRule } from '@wordpress/components'; -import { - InspectorControls, - withColors, - PanelColorSettings, -} from '@wordpress/block-editor'; +import { withColors } from '@wordpress/block-editor'; +/** + * Internal dependencies + */ +import SeparatorSettings from './separator-settings'; function SeparatorEdit( { color, setColor, className } ) { return ( @@ -29,19 +28,10 @@ function SeparatorEdit( { color, setColor, className } ) { color: color.color, } } /> - <InspectorControls> - <PanelColorSettings - title={ __( 'Color Settings' ) } - colorSettings={ [ - { - value: color.color, - onChange: setColor, - label: __( 'Color' ), - }, - ] } - > - </PanelColorSettings> - </InspectorControls> + <SeparatorSettings + color={ color } + setColor={ setColor } + /> </> ); } diff --git a/packages/block-library/src/separator/separator-settings.js b/packages/block-library/src/separator/separator-settings.js new file mode 100644 index 00000000000000..bb3a3a57aa1fa0 --- /dev/null +++ b/packages/block-library/src/separator/separator-settings.js @@ -0,0 +1,26 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + InspectorControls, + PanelColorSettings, +} from '@wordpress/block-editor'; + +const SeparatorSettings = ( { color, setColor } ) => ( + <InspectorControls> + <PanelColorSettings + title={ __( 'Color Settings' ) } + colorSettings={ [ + { + value: color.color, + onChange: setColor, + label: __( 'Color' ), + }, + ] } + > + </PanelColorSettings> + </InspectorControls> +); + +export default SeparatorSettings; diff --git a/packages/block-library/src/separator/separator-settings.native.js b/packages/block-library/src/separator/separator-settings.native.js new file mode 100644 index 00000000000000..d2bdd8ef6443a3 --- /dev/null +++ b/packages/block-library/src/separator/separator-settings.native.js @@ -0,0 +1,3 @@ +// Mobile has no separator settings at this time, so render nothing +const SeparatorSettings = () => null; +export default SeparatorSettings; diff --git a/packages/block-library/src/video/edit.native.js b/packages/block-library/src/video/edit.native.js index 3b0a9d186d3084..9d004832d37f69 100644 --- a/packages/block-library/src/video/edit.native.js +++ b/packages/block-library/src/video/edit.native.js @@ -20,7 +20,10 @@ import { Icon, Toolbar, ToolbarButton, + withTheme, + useStyle, } from '@wordpress/components'; + import { Caption, MediaPlaceholder, @@ -147,11 +150,13 @@ class VideoEdit extends React.Component { } getIcon( isRetryIcon, isMediaPlaceholder ) { + const iconStyle = useStyle( style.icon, style.iconDark, this.props.theme ); + if ( isRetryIcon ) { return <Icon icon={ SvgIconRetry } { ...style.icon } />; } - return <Icon icon={ SvgIcon } { ...( ! isMediaPlaceholder ? style.iconUploading : style.icon ) } />; + return <Icon icon={ SvgIcon } { ...( ! isMediaPlaceholder ? style.iconUploading : iconStyle ) } />; } render() { @@ -262,4 +267,4 @@ class VideoEdit extends React.Component { } } -export default VideoEdit; +export default withTheme( VideoEdit ); diff --git a/packages/block-library/src/video/style.native.scss b/packages/block-library/src/video/style.native.scss index dd4d70ae0a4752..5eb36be46605e3 100644 --- a/packages/block-library/src/video/style.native.scss +++ b/packages/block-library/src/video/style.native.scss @@ -59,6 +59,10 @@ height: 100%; } +.iconDark { + fill: $white; +} + .iconUploading { fill: $gray-lighten-20; width: 100%; diff --git a/packages/components/src/button/index.native.js b/packages/components/src/button/index.native.js index 7a90dd53131661..78d10226e26def 100644 --- a/packages/components/src/button/index.native.js +++ b/packages/components/src/button/index.native.js @@ -20,7 +20,6 @@ const styles = StyleSheet.create( { justifyContent: 'center', alignItems: 'center', aspectRatio: 1, - backgroundColor: 'white', }, buttonActive: { flex: 1, @@ -63,6 +62,7 @@ export default function Button( props ) { } = props; const isDisabled = ariaDisabled || disabled; + const buttonViewStyle = { opacity: isDisabled ? 0.2 : 1, ...( ariaPressed ? styles.buttonActive : styles.buttonInactive ), diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index 4984cdde2fd36d..d09b157160003c 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -27,3 +27,4 @@ export { default as KeyboardAvoidingView } from './mobile/keyboard-avoiding-view export { default as KeyboardAwareFlatList } from './mobile/keyboard-aware-flat-list'; export { default as Picker } from './mobile/picker'; export { default as ReadableContentView } from './mobile/readable-content-view'; +export * from './mobile/dark-mode'; diff --git a/packages/components/src/mobile/bottom-sheet/cell.native.js b/packages/components/src/mobile/bottom-sheet/cell.native.js index cebfc6a91c1826..cbc10928478704 100644 --- a/packages/components/src/mobile/bottom-sheet/cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/cell.native.js @@ -16,8 +16,10 @@ import { __, _x, sprintf } from '@wordpress/i18n'; */ import styles from './styles.scss'; import platformStyles from './cellStyles.scss'; +// `useStyle as getStyle`: Hack to avoid lint thinking this is a React Hook +import { withTheme, useStyle as getStyle } from '../dark-mode'; -export default class BottomSheetCell extends Component { +class BottomSheetCell extends Component { constructor( props ) { super( ...arguments ); this.state = { @@ -48,12 +50,15 @@ export default class BottomSheetCell extends Component { editable = true, separatorType, style = {}, + theme, ...valueProps } = this.props; const showValue = value !== undefined; const isValueEditable = editable && onChangeValue !== undefined; - const defaultLabelStyle = showValue || icon !== undefined ? styles.cellLabel : styles.cellLabelCentered; + const cellLabelStyle = getStyle( styles.cellLabel, styles.cellTextDark, theme ); + const cellLabelCenteredStyle = getStyle( styles.cellLabelCentered, styles.cellTextDark, theme ); + const defaultLabelStyle = showValue || icon !== undefined ? cellLabelStyle : cellLabelCenteredStyle; const drawSeparator = ( separatorType && separatorType !== 'none' ) || separatorStyle === undefined; const onCellPress = () => { @@ -75,22 +80,26 @@ export default class BottomSheetCell extends Component { }; const separatorStyle = () => { - const leftMarginStyle = { ...styles.cellSeparator, ...platformStyles.separatorMarginLeft }; + //eslint-disable-next-line @wordpress/no-unused-vars-before-return + const defaultSeparatorStyle = getStyle( styles.separator, styles.separatorDark, theme ); + const cellSeparatorStyle = getStyle( styles.cellSeparator, styles.cellSeparatorDark, theme ); + const leftMarginStyle = { ...cellSeparatorStyle, ...platformStyles.separatorMarginLeft }; switch ( separatorType ) { case 'leftMargin': return leftMarginStyle; case 'fullWidth': - return styles.separator; + return defaultSeparatorStyle; case 'none': return undefined; case undefined: - return showValue ? leftMarginStyle : styles.separator; + return showValue ? leftMarginStyle : defaultSeparatorStyle; } }; const getValueComponent = () => { const styleRTL = I18nManager.isRTL && styles.cellValueRTL; - const finalStyle = { ...styles.cellValue, ...valueStyle, ...styleRTL }; + const cellValueStyle = getStyle( styles.cellValue, styles.cellTextDark, theme ); + const finalStyle = { ...cellValueStyle, ...valueStyle, ...styleRTL }; // To be able to show the `middle` ellipsizeMode on editable cells // we show the TextInput just when the user wants to edit the value, @@ -114,7 +123,7 @@ export default class BottomSheetCell extends Component { /> ) : ( <Text - style={ { ...styles.cellValue, ...valueStyle } } + style={ { ...cellValueStyle, ...valueStyle } } numberOfLines={ 1 } ellipsizeMode={ 'middle' } > @@ -142,6 +151,8 @@ export default class BottomSheetCell extends Component { ); }; + const iconStyle = getStyle( styles.icon, styles.iconDark, theme ); + return ( <TouchableOpacity accessible={ ! this.state.isEditingValue } @@ -159,7 +170,7 @@ export default class BottomSheetCell extends Component { <View style={ styles.cellRowContainer }> { icon && ( <View style={ styles.cellRowContainer }> - <Dashicon icon={ icon } size={ 24 } /> + <Dashicon icon={ icon } size={ 24 } color={ iconStyle.color } /> <View style={ platformStyles.labelIconSeparator } /> </View> ) } @@ -177,3 +188,5 @@ export default class BottomSheetCell extends Component { ); } } + +export default withTheme( BottomSheetCell ); diff --git a/packages/components/src/mobile/bottom-sheet/index.native.js b/packages/components/src/mobile/bottom-sheet/index.native.js index 9106709cea25ef..e3209713e49449 100644 --- a/packages/components/src/mobile/bottom-sheet/index.native.js +++ b/packages/components/src/mobile/bottom-sheet/index.native.js @@ -19,6 +19,7 @@ import Cell from './cell'; import PickerCell from './picker-cell'; import SwitchCell from './switch-cell'; import KeyboardAvoidingView from './keyboard-avoiding-view'; +import { withTheme, useStyle } from '../dark-mode'; class BottomSheet extends Component { constructor() { @@ -63,6 +64,7 @@ class BottomSheet extends Component { hideHeader, style = {}, contentStyle = {}, + theme, } = this.props; const panResponder = PanResponder.create( { @@ -118,6 +120,8 @@ class BottomSheet extends Component { }, }; + const backgroundStyle = useStyle( styles.background, styles.backgroundDark, theme ); + return ( <Modal isVisible={ isVisible } @@ -139,7 +143,7 @@ class BottomSheet extends Component { > <KeyboardAvoidingView behavior={ Platform.OS === 'ios' && 'padding' } - style={ { ...styles.background, borderColor: 'rgba(0, 0, 0, 0.1)', ...style } } + style={ { ...backgroundStyle, borderColor: 'rgba(0, 0, 0, 0.1)', ...style } } keyboardVerticalOffset={ -this.state.safeAreaBottomInset } > <View style={ styles.dragIndicator } /> @@ -160,10 +164,12 @@ function getWidth() { return Math.min( Dimensions.get( 'window' ).width, styles.background.maxWidth ); } -BottomSheet.getWidth = getWidth; -BottomSheet.Button = Button; -BottomSheet.Cell = Cell; -BottomSheet.PickerCell = PickerCell; -BottomSheet.SwitchCell = SwitchCell; +const ThemedBottomSheet = withTheme( BottomSheet ); + +ThemedBottomSheet.getWidth = getWidth; +ThemedBottomSheet.Button = Button; +ThemedBottomSheet.Cell = Cell; +ThemedBottomSheet.PickerCell = PickerCell; +ThemedBottomSheet.SwitchCell = SwitchCell; -export default BottomSheet; +export default ThemedBottomSheet; diff --git a/packages/components/src/mobile/bottom-sheet/styles.native.scss b/packages/components/src/mobile/bottom-sheet/styles.native.scss index 53764ee4fe38f1..8f153715c16705 100644 --- a/packages/components/src/mobile/bottom-sheet/styles.native.scss +++ b/packages/components/src/mobile/bottom-sheet/styles.native.scss @@ -21,6 +21,10 @@ width: 100%; } +.separatorDark { + background-color: $gray-70; +} + .emptyHeaderSpace { height: 14; } @@ -34,6 +38,10 @@ padding-bottom: 0; } +.backgroundDark { + background-color: $background-dark-elevated; +} + .content { padding: 0 16px 0 16px; } @@ -86,6 +94,10 @@ width: 100%; } +.cellSeparatorDark { + background-color: $gray-70; +} + .cellRowContainer { flex-direction: row; align-items: center; @@ -115,6 +127,18 @@ flex: 1; } +.cellTextDark { + color: $white; +} + .cellValueRTL { text-align: left; } + +.icon { + color: #7b9ab1; +} + +.iconDark { + color: #c3c4c7; +} diff --git a/packages/components/src/mobile/dark-mode/index.native.js b/packages/components/src/mobile/dark-mode/index.native.js new file mode 100644 index 00000000000000..d2b13020f027f9 --- /dev/null +++ b/packages/components/src/mobile/dark-mode/index.native.js @@ -0,0 +1,53 @@ +/** + * External dependencies + */ +import { eventEmitter, initialMode } from 'react-native-dark-mode'; +import React from 'react'; + +// This was failing on CI +if ( eventEmitter.setMaxListeners ) { + eventEmitter.setMaxListeners( 150 ); +} + +export function useStyle( light, dark, theme ) { + const finalDark = { + ...light, + ...dark, + }; + + return theme === 'dark' ? finalDark : light; +} + +// This function takes a component... +export function withTheme( WrappedComponent ) { + return class extends React.Component { + constructor( props ) { + super( props ); + + this.onModeChanged = this.onModeChanged.bind( this ); + + this.state = { + mode: initialMode, + }; + } + + onModeChanged( newMode ) { + this.setState( { mode: newMode } ); + } + + componentDidMount() { + this.subscription = eventEmitter.on( 'currentModeChanged', this.onModeChanged ); + } + + componentWillUnmount() { + // Conditional needed to pass UI Tests on CI + if ( eventEmitter.removeListener ) { + eventEmitter.removeListener( 'currentModeChanged', this.onModeChanged ); + } + } + + render() { + return <WrappedComponent theme={ this.state.mode } { ...this.props } />; + } + }; +} diff --git a/packages/components/src/mobile/html-text-input/index.native.js b/packages/components/src/mobile/html-text-input/index.native.js index e25f7a1af71c4d..1408be65c90530 100644 --- a/packages/components/src/mobile/html-text-input/index.native.js +++ b/packages/components/src/mobile/html-text-input/index.native.js @@ -15,6 +15,7 @@ import { withInstanceId, compose } from '@wordpress/compose'; /** * Internal dependencies */ +import { withTheme, useStyle } from '../dark-mode'; import HTMLInputContainer from './container'; import styles from './style.scss'; @@ -60,6 +61,8 @@ export class HTMLTextInput extends Component { } render() { + const htmlStyle = useStyle( styles.htmlView, styles.htmlViewDark, this.props.theme ); + const placeholderStyle = useStyle( styles.placeholder, styles.placeholderDark, this.props.theme ); return ( <HTMLInputContainer parentHeight={ this.props.parentHeight }> <TextInput @@ -70,6 +73,7 @@ export class HTMLTextInput extends Component { style={ styles.htmlViewTitle } value={ this.props.title } placeholder={ __( 'Add title' ) } + placeholderTextColor={ placeholderStyle.color } onChangeText={ this.props.editTitle } /> <TextInput @@ -77,11 +81,12 @@ export class HTMLTextInput extends Component { accessibilityLabel="html-view-content" textAlignVertical="top" multiline - style={ styles.htmlView } + style={ htmlStyle } value={ this.state.value } onChangeText={ this.edit } onBlur={ this.stopEditing } placeholder={ __( 'Start writing…' ) } + placeholderTextColor={ placeholderStyle.color } scrollEnabled={ HTMLInputContainer.scrollEnabled } /> </HTMLInputContainer> @@ -117,4 +122,5 @@ export default compose( [ }; } ), withInstanceId, + withTheme, ] )( HTMLTextInput ); diff --git a/packages/components/src/mobile/html-text-input/style-common.native.scss b/packages/components/src/mobile/html-text-input/style-common.native.scss index 4db5b985161402..c1ac9f155d4c79 100644 --- a/packages/components/src/mobile/html-text-input/style-common.native.scss +++ b/packages/components/src/mobile/html-text-input/style-common.native.scss @@ -1,6 +1,6 @@ $padding: 8; -$backgroundColor: $white; $htmlFont: $default-monospace-font; +$textColorDark: $white; .keyboardAvoidingView { position: absolute; @@ -13,3 +13,11 @@ $htmlFont: $default-monospace-font; .container { flex: 1; } + +.placeholder { + color: $gray; +} + +.placeholderDark { + color: $gray-50; +} diff --git a/packages/components/src/mobile/html-text-input/style.android.scss b/packages/components/src/mobile/html-text-input/style.android.scss index 10594358722c37..1dca01274d75b5 100644 --- a/packages/components/src/mobile/html-text-input/style.android.scss +++ b/packages/components/src/mobile/html-text-input/style.android.scss @@ -2,7 +2,6 @@ .htmlView { font-family: $htmlFont; - background-color: $backgroundColor; padding-left: $padding; padding-right: $padding; padding-top: $padding; @@ -11,7 +10,6 @@ .htmlViewTitle { font-family: $htmlFont; - background-color: $backgroundColor; padding-left: $padding; padding-right: $padding; padding-top: $padding; diff --git a/packages/components/src/mobile/html-text-input/style.ios.scss b/packages/components/src/mobile/html-text-input/style.ios.scss index 8b13392b95a9ae..97cf00a7512ff5 100644 --- a/packages/components/src/mobile/html-text-input/style.ios.scss +++ b/packages/components/src/mobile/html-text-input/style.ios.scss @@ -4,15 +4,17 @@ $title-height: 32; .htmlView { font-family: $htmlFont; - background-color: $backgroundColor; padding-left: $padding; padding-right: $padding; padding-bottom: $title-height + $padding; } +.htmlViewDark { + color: $textColorDark; +} + .htmlViewTitle { font-family: $htmlFont; - background-color: $backgroundColor; padding-left: $padding; padding-right: $padding; padding-top: $padding; diff --git a/packages/components/src/primitives/svg/style.native.scss b/packages/components/src/primitives/svg/style.native.scss index e5a64ea140d0d8..595372b06329e1 100644 --- a/packages/components/src/primitives/svg/style.native.scss +++ b/packages/components/src/primitives/svg/style.native.scss @@ -13,12 +13,22 @@ fill: currentColor; } -.unsupported-icon { +.unsupported-icon-light { color: $gray-dark; fill: currentColor; } -.warning-icon { +.unsupported-icon-dark { + color: $white; + fill: currentColor; +} + +.warning-icon-light { color: $gray-dark; fill: currentColor; } + +.warning-icon-dark { + color: $white; + fill: currentColor; +} diff --git a/packages/components/src/toolbar/style.native.scss b/packages/components/src/toolbar/style.native.scss index 1e0d176e275d80..3038ea8e491d8d 100644 --- a/packages/components/src/toolbar/style.native.scss +++ b/packages/components/src/toolbar/style.native.scss @@ -5,3 +5,7 @@ padding-left: 5px; padding-right: 5px; } + +.containerDark { + border-color: #515459; +} diff --git a/packages/components/src/toolbar/toolbar-container.native.js b/packages/components/src/toolbar/toolbar-container.native.js index 887991d2ea1238..f9d019450266c2 100644 --- a/packages/components/src/toolbar/toolbar-container.native.js +++ b/packages/components/src/toolbar/toolbar-container.native.js @@ -7,11 +7,12 @@ import { View } from 'react-native'; * Internal dependencies */ import styles from './style.scss'; +import { withTheme, useStyle } from '../mobile/dark-mode'; const ToolbarContainer = ( props ) => ( - <View style={ [ styles.container, props.passedStyle ] }> + <View style={ [ useStyle( styles.container, styles.containerDark, props.theme ), props.passedStyle ] }> { props.children } </View> ); -export default ToolbarContainer; +export default withTheme( ToolbarContainer ); diff --git a/packages/edit-post/src/components/header/header-toolbar/index.native.js b/packages/edit-post/src/components/header/header-toolbar/index.native.js index 7c33856a629180..97ca3a62349bc5 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.native.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.native.js @@ -15,7 +15,7 @@ import { Inserter, BlockToolbar, } from '@wordpress/block-editor'; -import { Toolbar, ToolbarButton } from '@wordpress/components'; +import { Toolbar, ToolbarButton, useStyle, withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -31,6 +31,7 @@ function HeaderToolbar( { showInserter, showKeyboardHideButton, clearSelectedBlock, + theme, } ) { const scrollViewRef = useRef( null ); const scrollToStart = () => { @@ -38,7 +39,7 @@ function HeaderToolbar( { }; return ( - <View style={ styles.container }> + <View style={ useStyle( styles.container, styles.containerDark, theme ) }> <ScrollView ref={ scrollViewRef } onContentSizeChange={ scrollToStart } @@ -99,4 +100,5 @@ export default compose( [ clearSelectedBlock: dispatch( 'core/block-editor' ).clearSelectedBlock, } ) ), withViewportMatch( { isLargeViewport: 'medium' } ), + withTheme, ] )( HeaderToolbar ); diff --git a/packages/edit-post/src/components/header/header-toolbar/style.native.scss b/packages/edit-post/src/components/header/header-toolbar/style.native.scss index 0aa03b90ed81cc..9210ce5506d620 100644 --- a/packages/edit-post/src/components/header/header-toolbar/style.native.scss +++ b/packages/edit-post/src/components/header/header-toolbar/style.native.scss @@ -7,6 +7,11 @@ border-top-width: 1px; } +.containerDark { + background-color: $background-dark-elevated; + border-top-color: $background-dark-elevated; +} + .scrollableContent { flex-grow: 1; // Fixes RTL issue on Android. } diff --git a/packages/edit-post/src/components/layout/index.native.js b/packages/edit-post/src/components/layout/index.native.js index 3242628c021b89..5b6813067211d2 100644 --- a/packages/edit-post/src/components/layout/index.native.js +++ b/packages/edit-post/src/components/layout/index.native.js @@ -11,7 +11,7 @@ import { sendNativeEditorDidLayout } from 'react-native-gutenberg-bridge'; import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; -import { HTMLTextInput, KeyboardAvoidingView, ReadableContentView } from '@wordpress/components'; +import { HTMLTextInput, KeyboardAvoidingView, ReadableContentView, useStyle, withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -75,7 +75,7 @@ class Layout extends Component { renderHTML() { return ( - <HTMLTextInput /> + <HTMLTextInput parentHeight={ this.state.rootViewHeight } /> ); } @@ -101,8 +101,10 @@ class Layout extends Component { mode, } = this.props; + const isHtmlView = mode === 'text'; + // add a margin view at the bottom for the header - const marginBottom = Platform.OS === 'android' ? headerToolbarStyles.container.height : 0; + const marginBottom = Platform.OS === 'android' && ! isHtmlView ? headerToolbarStyles.container.height : 0; const toolbarKeyboardAvoidingViewStyle = { ...styles.toolbarKeyboardAvoidingView, @@ -112,18 +114,19 @@ class Layout extends Component { }; return ( - <SafeAreaView style={ styles.container } onLayout={ this.onRootViewLayout }> - <View style={ { flex: 1 } }> - { mode === 'text' ? this.renderHTML() : this.renderVisual() } + <SafeAreaView style={ useStyle( styles.container, styles.containerDark, this.props.theme ) } onLayout={ this.onRootViewLayout }> + <View style={ useStyle( styles.background, styles.backgroundDark, this.props.theme ) }> + { isHtmlView ? this.renderHTML() : this.renderVisual() } </View> <View style={ { flex: 0, flexBasis: marginBottom, height: marginBottom } }> </View> - <KeyboardAvoidingView - parentHeight={ this.state.rootViewHeight } - style={ toolbarKeyboardAvoidingViewStyle } - > - <Header /> - </KeyboardAvoidingView> + { ! isHtmlView && ( + <KeyboardAvoidingView + parentHeight={ this.state.rootViewHeight } + style={ toolbarKeyboardAvoidingViewStyle } + > + <Header /> + </KeyboardAvoidingView> ) } </SafeAreaView> ); } @@ -143,4 +146,5 @@ export default compose( [ mode: getEditorMode(), }; } ), + withTheme, ] )( Layout ); diff --git a/packages/edit-post/src/components/layout/style.native.scss b/packages/edit-post/src/components/layout/style.native.scss index e6d7e241bcd0df..7a5026d6664dc9 100644 --- a/packages/edit-post/src/components/layout/style.native.scss +++ b/packages/edit-post/src/components/layout/style.native.scss @@ -5,6 +5,19 @@ background-color: #fff; } +.containerDark { + background-color: $background-dark-elevated; +} + +.background { + flex: 1; + background-color: $white; +} + +.backgroundDark { + background-color: $black; +} + .toolbarKeyboardAvoidingView { position: absolute; bottom: 0; diff --git a/packages/edit-post/src/components/visual-editor/index.native.js b/packages/edit-post/src/components/visual-editor/index.native.js index 15ca4ed9d451bc..9d1925356a9138 100644 --- a/packages/edit-post/src/components/visual-editor/index.native.js +++ b/packages/edit-post/src/components/visual-editor/index.native.js @@ -7,7 +7,7 @@ import { withDispatch, withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import { BlockList } from '@wordpress/block-editor'; import { PostTitle } from '@wordpress/editor'; -import { ReadableContentView } from '@wordpress/components'; +import { ReadableContentView, withTheme, useStyle } from '@wordpress/components'; /** * Internal dependencies @@ -20,8 +20,9 @@ class VisualEditor extends Component { editTitle, setTitleRef, title, + theme, } = this.props; - + const blockHolderFocusedStyle = useStyle( styles.blockHolderFocused, styles.blockHolderFocusedDark, theme ); return ( <ReadableContentView> <PostTitle @@ -34,7 +35,7 @@ class VisualEditor extends Component { styles.blockHolderFullBordered : styles.blockHolderSemiBordered } - focusedBorderColor={ styles.blockHolderFocused.borderColor } + focusedBorderColor={ blockHolderFocusedStyle.borderColor } accessibilityLabel="post-title" /> </ReadableContentView> @@ -81,4 +82,5 @@ export default compose( [ }, }; } ), + withTheme, ] )( VisualEditor ); diff --git a/packages/edit-post/src/components/visual-editor/style.native.scss b/packages/edit-post/src/components/visual-editor/style.native.scss index 02b49a1515584c..4ade220b5dd9e9 100644 --- a/packages/edit-post/src/components/visual-editor/style.native.scss +++ b/packages/edit-post/src/components/visual-editor/style.native.scss @@ -15,3 +15,7 @@ .blockHolderFocused { border-color: $gray-lighten-30; } + +.blockHolderFocusedDark { + border-color: $gray-70; +} diff --git a/packages/edit-post/src/editor.native.js b/packages/edit-post/src/editor.native.js index 23e0bad6a9e60a..87acaaf96669c0 100644 --- a/packages/edit-post/src/editor.native.js +++ b/packages/edit-post/src/editor.native.js @@ -3,6 +3,7 @@ */ import memize from 'memize'; import { size, map, without } from 'lodash'; +import { subscribeSetFocusOnTitle } from 'react-native-gutenberg-bridge'; /** * WordPress dependencies @@ -31,6 +32,8 @@ class Editor extends Component { this.getEditorSettings = memize( this.getEditorSettings, { maxSize: 1, } ); + + this.setTitleRef = this.setTitleRef.bind( this ); } getEditorSettings( @@ -66,6 +69,24 @@ class Editor extends Component { return settings; } + componentDidMount() { + this.subscriptionParentSetFocusOnTitle = subscribeSetFocusOnTitle( () => { + if ( this.postTitleRef ) { + this.postTitleRef.focus(); + } + } ); + } + + componentWillUnmount() { + if ( this.subscriptionParentSetFocusOnTitle ) { + this.subscriptionParentSetFocusOnTitle.remove(); + } + } + + setTitleRef( titleRef ) { + this.postTitleRef = titleRef; + } + render() { const { settings, diff --git a/packages/editor/src/components/provider/index.native.js b/packages/editor/src/components/provider/index.native.js index d19da119ef0ba5..7a055f438f4d91 100644 --- a/packages/editor/src/components/provider/index.native.js +++ b/packages/editor/src/components/provider/index.native.js @@ -5,7 +5,6 @@ import RNReactNativeGutenbergBridge, { subscribeParentGetHtml, subscribeParentToggleHTMLMode, subscribeUpdateHtml, - subscribeSetFocusOnTitle, subscribeSetTitle, } from 'react-native-gutenberg-bridge'; @@ -28,8 +27,6 @@ class NativeEditorProvider extends Component { // Keep a local reference to `post` to detect changes this.post = props.post; - - this.setTitleRef = this.setTitleRef.bind( this ); } componentDidMount() { @@ -48,12 +45,6 @@ class NativeEditorProvider extends Component { this.subscriptionParentUpdateHtml = subscribeUpdateHtml( ( payload ) => { this.updateHtmlAction( payload.html ); } ); - - this.subscriptionParentSetFocusOnTitle = subscribeSetFocusOnTitle( () => { - if ( this.postTitleRef ) { - this.postTitleRef.focus(); - } - } ); } componentWillUnmount() { @@ -72,10 +63,6 @@ class NativeEditorProvider extends Component { if ( this.subscriptionParentUpdateHtml ) { this.subscriptionParentUpdateHtml.remove(); } - - if ( this.subscriptionParentSetFocusOnTitle ) { - this.subscriptionParentSetFocusOnTitle.remove(); - } } componentDidUpdate( prevProps ) { @@ -87,10 +74,6 @@ class NativeEditorProvider extends Component { } } - setTitleRef( titleRef ) { - this.postTitleRef = titleRef; - } - serializeToNativeAction() { if ( this.props.mode === 'text' ) { this.updateHtmlAction( this.props.getEditedPostContent() ); diff --git a/packages/format-library/src/link/test/modal.native.js b/packages/format-library/src/link/test/modal.native.js index 03594a4ef1c380..44153ea3a40185 100644 --- a/packages/format-library/src/link/test/modal.native.js +++ b/packages/format-library/src/link/test/modal.native.js @@ -23,7 +23,7 @@ describe( 'LinksUI', () => { onRemove={ onRemove } onClose={ jest.fn() } /> - ).dive(); // -> dive() removes the HOC layer that was blocking access to ModalLinkUI + ).dive().dive(); // -> dive() removes the HOC layer that was blocking access to ModalLinkUI // When @@ -52,8 +52,10 @@ describe( 'LinksUI', () => { // When // Simulate user typing on the URL Cell. - const bottomSheet = wrapper.find( 'BottomSheet' ).first(); - const cell = bottomSheet.find( 'BottomSheetCell' ).first(); + const bottomSheet = wrapper.dive().find( 'BottomSheet' ).first(); + // withTheme is type "_class", we search for it and dive into BottomSheetCell + const cell = bottomSheet.dive().find( '_class' ).first().dive(); + cell.simulate( 'changeValue', 'wordpress.com' ); // Close the BottomSheet diff --git a/packages/rich-text/src/component/index.native.js b/packages/rich-text/src/component/index.native.js index fe705d8ad5b386..6191e627d56962 100644 --- a/packages/rich-text/src/component/index.native.js +++ b/packages/rich-text/src/component/index.native.js @@ -20,6 +20,7 @@ import { childrenBlock } from '@wordpress/blocks'; import { decodeEntities } from '@wordpress/html-entities'; import { BACKSPACE } from '@wordpress/keycodes'; import { isURL } from '@wordpress/url'; +import { useStyle, withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -770,25 +771,28 @@ export class RichText extends Component { style, __unstableIsSelected: isSelected, children, + theme, } = this.props; const record = this.getRecord(); const html = this.getHtmlToRender( record, tagName ); - let minHeight = styles[ 'rich-text' ].minHeight; + let minHeight = styles.richText.minHeight; if ( style && style.minHeight ) { minHeight = style.minHeight; } + const placeholderStyle = useStyle( styles.richTextPlaceholder, styles.richTextPlaceholderDark, theme ); + const { color: defaultPlaceholderTextColor, - } = styles[ 'rich-text-placeholder' ]; + } = placeholderStyle; const { color: defaultColor, textDecorationColor: defaultTextDecorationColor, fontFamily: defaultFontFamily, - } = styles[ 'rich-text' ]; + } = useStyle( styles.richText, styles.richTextDark, theme ); let selection = null; if ( this.needsSelectionUpdate ) { @@ -817,6 +821,8 @@ export class RichText extends Component { this.firedAfterTextChanged = false; } + const dynamicStyle = useStyle( style, styles.richTextDark, theme ); + return ( <View> { children && children( { @@ -833,7 +839,7 @@ export class RichText extends Component { } } } style={ { - ...style, + ...dynamicStyle, minHeight: Math.max( minHeight, this.state.height ), } } text={ { text: html, eventCount: this.lastEventCount, selection } } @@ -878,4 +884,5 @@ export default compose( [ withSelect( ( select ) => ( { formatTypes: select( 'core/rich-text' ).getFormatTypes(), } ) ), + withTheme, ] )( RichText ); diff --git a/packages/rich-text/src/component/style.native.scss b/packages/rich-text/src/component/style.native.scss index 6481c415694127..4ed93f7f70239d 100644 --- a/packages/rich-text/src/component/style.native.scss +++ b/packages/rich-text/src/component/style.native.scss @@ -1,11 +1,21 @@ -.rich-text { +.richText { font-family: $default-regular-font; min-height: $min-height-paragraph; color: $gray-900; text-decoration-color: $blue-500; } -.rich-text-placeholder { +.richTextDark { + color: $white; + text-decoration-color: $blue-30; + background-color: $black; +} + +.richTextPlaceholder { color: $gray; } + +.richTextPlaceholderDark { + color: $gray-50; +} diff --git a/test/native/__mocks__/react-native-dark-mode/index.js b/test/native/__mocks__/react-native-dark-mode/index.js new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/native/__mocks__/styleMock.js b/test/native/__mocks__/styleMock.js index 182af41388dc0a..4e63b494feff94 100644 --- a/test/native/__mocks__/styleMock.js +++ b/test/native/__mocks__/styleMock.js @@ -15,7 +15,7 @@ module.exports = { blockCode: { fontFamily: 'serif', }, - 'rich-text': { + richText: { fontFamily: 'serif', minHeight: 30, }, @@ -66,4 +66,13 @@ module.exports = { iconUploading: { fill: 'gray', }, + placeholder: { + color: 'gray', + }, + richTextPlaceholder: { + color: 'gray', + }, + unsupportedBlockIcon: { + color: 'white', + }, }; diff --git a/test/native/setup.js b/test/native/setup.js index cfd2417a11ad9d..c0e3e0942a9180 100644 --- a/test/native/setup.js +++ b/test/native/setup.js @@ -21,6 +21,15 @@ jest.mock( 'react-native-gutenberg-bridge', () => { }; } ); +jest.mock( 'react-native-dark-mode', () => { + return { + eventEmitter: { + on: jest.fn(), + }, + initialMode: 'light', + }; +} ); + jest.mock( 'react-native-modal', () => () => 'Modal' ); jest.mock( 'react-native-hr', () => () => 'Hr' ); From 643c1b2b3a43e2d977acdfe63b7f3eb14c0c53f1 Mon Sep 17 00:00:00 2001 From: etoledom <etoledom@icloud.com> Date: Wed, 28 Aug 2019 12:16:19 +0200 Subject: [PATCH 09/31] Activate Travis CI on rnmobile/master branch (#17229) --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 3e16c463eb6ed5..4a9ab1b7fe9429 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ cache: branches: only: - master + - rnmobile/master before_install: - nvm install From a78f204df645ce86890814796b41498248f70dad Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler <dekervit@gmail.com> Date: Thu, 29 Aug 2019 16:53:06 +0200 Subject: [PATCH 10/31] Add native support for the MediaText block (#16305) * First working version of the MediaText component for native mobile * Fix adding a block to an innerblock list * Disable mediaText on production * MediaText native: improve editor visuals * Move BlockToolbar from BlockList to Layout * Remove BlockEditorProvider from BlockList and add native version of EditorProvider to Editor. Plus support InsertionPoint and BlockListAppender * Update BlockMover for native to hide if locked or if it's the only block * Make the vertical align button work, add more styling options for toolbar buttons * Make sure registerCoreBlocks does not break in production * Copy docblock comment from the web version for registerCoreBlocks * Fix focusing on the media placeholder * Only support adding image for now * Update usage of MediaPlaceholder in MediaContainer * Enable autoScroll for just the out most block list * Fix JS Unit tests * Roll back to IconButton refactor and fix tests * Fix BlockVerticalAlignmentToolbar buttons style on mobile * Fix thing for web and ensure ariaPressed is always passed down * Use AriaPressed directly to style SVG on mobile * Update snapshots --- .../src/components/block-icon/index.native.js | 30 +++ .../block-list-appender/index.native.js | 54 +++++ .../block-list-appender/style.native.scss | 9 + .../src/components/block-list/index.native.js | 1 + .../block-list/insertion-point.native.js | 46 +++++ .../components/block-mover/index.native.js | 82 ++++---- .../src/components/index.native.js | 3 + .../components/inner-blocks/index.native.js | 182 +++++++++++++++++ .../src/components/url-input/test/button.js | 8 +- packages/block-library/src/index.native.js | 42 +++- .../src/media-text/edit.native.js | 186 +++++++++++++++++ .../src/media-text/media-container.native.js | 191 ++++++++++++++++++ .../src/media-text/style.native.scss | 29 +++ packages/blocks/src/api/index.native.js | 4 + packages/components/src/icon-button/index.js | 6 +- .../components/src/icon-button/test/index.js | 2 +- packages/components/src/icon/index.js | 20 +- packages/components/src/icon/test/index.js | 10 +- .../keyboard-aware-flat-list/index.ios.js | 2 + .../src/primitives/svg/index.native.js | 4 +- .../src/primitives/svg/style.native.scss | 6 +- .../test/__snapshots__/index.js.snap | 45 +++-- .../components/visual-editor/index.native.js | 1 + 23 files changed, 879 insertions(+), 84 deletions(-) create mode 100644 packages/block-editor/src/components/block-icon/index.native.js create mode 100644 packages/block-editor/src/components/block-list-appender/index.native.js create mode 100644 packages/block-editor/src/components/block-list-appender/style.native.scss create mode 100644 packages/block-editor/src/components/block-list/insertion-point.native.js create mode 100644 packages/block-editor/src/components/inner-blocks/index.native.js create mode 100644 packages/block-library/src/media-text/edit.native.js create mode 100644 packages/block-library/src/media-text/media-container.native.js create mode 100644 packages/block-library/src/media-text/style.native.scss diff --git a/packages/block-editor/src/components/block-icon/index.native.js b/packages/block-editor/src/components/block-icon/index.native.js new file mode 100644 index 00000000000000..4dddda5ecce8f0 --- /dev/null +++ b/packages/block-editor/src/components/block-icon/index.native.js @@ -0,0 +1,30 @@ +/** + * External dependencies + */ +import { get } from 'lodash'; +import { View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { Path, Icon, SVG } from '@wordpress/components'; + +export default function BlockIcon( { icon, showColors = false } ) { + if ( get( icon, [ 'src' ] ) === 'block-default' ) { + icon = { + src: <SVG xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><Path d="M19 7h-1V5h-4v2h-4V5H6v2H5c-1.1 0-2 .9-2 2v10h18V9c0-1.1-.9-2-2-2zm0 10H5V9h14v8z" /></SVG>, + }; + } + + const renderedIcon = <Icon icon={ icon && icon.src ? icon.src : icon } />; + const style = showColors ? { + backgroundColor: icon && icon.background, + color: icon && icon.foreground, + } : {}; + + return ( + <View style={ style }> + { renderedIcon } + </View> + ); +} diff --git a/packages/block-editor/src/components/block-list-appender/index.native.js b/packages/block-editor/src/components/block-list-appender/index.native.js new file mode 100644 index 00000000000000..9a6a24c5575172 --- /dev/null +++ b/packages/block-editor/src/components/block-list-appender/index.native.js @@ -0,0 +1,54 @@ +/** + * External dependencies + */ +import { last } from 'lodash'; + +/** + * WordPress dependencies + */ +import { withSelect } from '@wordpress/data'; +import { getDefaultBlockName } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import DefaultBlockAppender from '../default-block-appender'; +import styles from './style.scss'; + +function BlockListAppender( { + blockClientIds, + rootClientId, + canInsertDefaultBlock, + isLocked, +} ) { + if ( isLocked ) { + return null; + } + + if ( canInsertDefaultBlock ) { + return ( + <DefaultBlockAppender + rootClientId={ rootClientId } + lastBlockClientId={ last( blockClientIds ) } + containerStyle={ styles.blockListAppender } + placeholder={ blockClientIds.length > 0 ? '' : null } + /> + ); + } + + return null; +} + +export default withSelect( ( select, { rootClientId } ) => { + const { + getBlockOrder, + canInsertBlockType, + getTemplateLock, + } = select( 'core/block-editor' ); + + return { + isLocked: !! getTemplateLock( rootClientId ), + blockClientIds: getBlockOrder( rootClientId ), + canInsertDefaultBlock: canInsertBlockType( getDefaultBlockName(), rootClientId ), + }; +} )( BlockListAppender ); diff --git a/packages/block-editor/src/components/block-list-appender/style.native.scss b/packages/block-editor/src/components/block-list-appender/style.native.scss new file mode 100644 index 00000000000000..60734f5e9c2106 --- /dev/null +++ b/packages/block-editor/src/components/block-list-appender/style.native.scss @@ -0,0 +1,9 @@ + +.blockListAppender { + background-color: $white; + padding-left: 16; + padding-right: 16; + padding-top: 12; + padding-bottom: 0; // will be flushed into inline toolbar height + border-color: transparent; +} 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 10a5cfcbcf1bae..7c67de71c342f0 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -100,6 +100,7 @@ export class BlockList extends Component { <KeyboardAwareFlatList { ...( Platform.OS === 'android' ? { removeClippedSubviews: false } : {} ) } // Disable clipping on Android to fix focus losing. See https://github.com/wordpress-mobile/gutenberg-mobile/pull/741#issuecomment-472746541 accessibilityLabel="block-list" + autoScroll={ this.props.autoScroll } innerRef={ this.scrollViewInnerRef } extraScrollHeight={ innerToolbarHeight + 10 } keyboardShouldPersistTaps="always" diff --git a/packages/block-editor/src/components/block-list/insertion-point.native.js b/packages/block-editor/src/components/block-list/insertion-point.native.js new file mode 100644 index 00000000000000..d6caa88f407fba --- /dev/null +++ b/packages/block-editor/src/components/block-list/insertion-point.native.js @@ -0,0 +1,46 @@ +/** + * External dependencies + */ +import { Text, View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { withSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import styles from './style.scss'; + +const BlockInsertionPoint = ( { showInsertionPoint } ) => { + if ( ! showInsertionPoint ) { + return null; + } + + return ( + <View style={ styles.containerStyleAddHere } > + <View style={ styles.lineStyleAddHere }></View> + <Text style={ styles.labelStyleAddHere } >{ __( 'ADD BLOCK HERE' ) }</Text> + <View style={ styles.lineStyleAddHere }></View> + </View> + ); +}; + +export default withSelect( ( select, { clientId, rootClientId } ) => { + const { + getBlockIndex, + getBlockInsertionPoint, + isBlockInsertionPointVisible, + } = select( 'core/block-editor' ); + const blockIndex = getBlockIndex( clientId, rootClientId ); + const insertionPoint = getBlockInsertionPoint(); + const showInsertionPoint = ( + isBlockInsertionPointVisible() && + insertionPoint.index === blockIndex && + insertionPoint.rootClientId === rootClientId + ); + + return { showInsertionPoint }; +} )( BlockInsertionPoint ); diff --git a/packages/block-editor/src/components/block-mover/index.native.js b/packages/block-editor/src/components/block-mover/index.native.js index 8962dc22f49e45..40bc4d550b7ab6 100644 --- a/packages/block-editor/src/components/block-mover/index.native.js +++ b/packages/block-editor/src/components/block-mover/index.native.js @@ -14,51 +14,59 @@ import { withInstanceId, compose } from '@wordpress/compose'; const BlockMover = ( { isFirst, isLast, + isLocked, onMoveDown, onMoveUp, firstIndex, -} ) => ( - <> - <ToolbarButton - title={ ! isFirst ? - sprintf( - /* translators: accessibility text. %1: current block position (number). %2: next block position (number) */ - __( 'Move block up from row %1$s to row %2$s' ), - firstIndex + 1, - firstIndex - ) : - __( 'Move block up' ) - } - isDisabled={ isFirst } - onClick={ onMoveUp } - icon="arrow-up-alt" - extraProps={ { hint: __( 'Double tap to move the block up' ) } } - /> + rootClientId, +} ) => { + if ( isLocked || ( isFirst && isLast && ! rootClientId ) ) { + return null; + } - <ToolbarButton - title={ ! isLast ? - sprintf( - /* translators: accessibility text. %1: current block position (number). %2: next block position (number) */ - __( 'Move block down from row %1$s to row %2$s' ), - firstIndex + 1, - firstIndex + 2 - ) : - __( 'Move block down' ) - } - isDisabled={ isLast } - onClick={ onMoveDown } - icon="arrow-down-alt" - extraProps={ { hint: __( 'Double tap to move the block down' ) } } - /> - </> -); + return ( + <> + <ToolbarButton + title={ ! isFirst ? + sprintf( + /* translators: accessibility text. %1: current block position (number). %2: next block position (number) */ + __( 'Move block up from row %1$s to row %2$s' ), + firstIndex + 1, + firstIndex + ) : + __( 'Move block up' ) + } + isDisabled={ isFirst } + onClick={ onMoveUp } + icon="arrow-up-alt" + extraProps={ { hint: __( 'Double tap to move the block up' ) } } + /> + + <ToolbarButton + title={ ! isLast ? + sprintf( + /* translators: accessibility text. %1: current block position (number). %2: next block position (number) */ + __( 'Move block down from row %1$s to row %2$s' ), + firstIndex + 1, + firstIndex + 2 + ) : + __( 'Move block down' ) + } + isDisabled={ isLast } + onClick={ onMoveDown } + icon="arrow-down-alt" + extraProps={ { hint: __( 'Double tap to move the block down' ) } } + /> + </> + ); +}; export default compose( withSelect( ( select, { clientIds } ) => { - const { getBlockIndex, getBlockRootClientId, getBlockOrder } = select( 'core/block-editor' ); + const { getBlockIndex, getTemplateLock, getBlockRootClientId, getBlockOrder } = select( 'core/block-editor' ); const normalizedClientIds = castArray( clientIds ); const firstClientId = first( normalizedClientIds ); - const rootClientId = getBlockRootClientId( first( normalizedClientIds ) ); + const rootClientId = getBlockRootClientId( firstClientId ); const blockOrder = getBlockOrder( rootClientId ); const firstIndex = getBlockIndex( firstClientId, rootClientId ); const lastIndex = getBlockIndex( last( normalizedClientIds ), rootClientId ); @@ -67,6 +75,8 @@ export default compose( firstIndex, isFirst: firstIndex === 0, isLast: lastIndex === blockOrder.length - 1, + isLocked: getTemplateLock( rootClientId ) === 'all', + rootClientId, }; } ), withDispatch( ( dispatch, { clientIds, rootClientId } ) => { diff --git a/packages/block-editor/src/components/index.native.js b/packages/block-editor/src/components/index.native.js index 5f13de7d91b4ce..7c6906da0066a7 100644 --- a/packages/block-editor/src/components/index.native.js +++ b/packages/block-editor/src/components/index.native.js @@ -2,9 +2,12 @@ export { default as BlockControls } from './block-controls'; export { default as BlockEdit } from './block-edit'; export { default as BlockFormatControls } from './block-format-controls'; +export { default as BlockIcon } from './block-icon'; +export { default as BlockVerticalAlignmentToolbar } from './block-vertical-alignment-toolbar'; export * from './colors'; export * from './font-sizes'; export { default as AlignmentToolbar } from './alignment-toolbar'; +export { default as InnerBlocks } from './inner-blocks'; export { default as InspectorControls } from './inspector-controls'; export { default as PlainText } from './plain-text'; export { diff --git a/packages/block-editor/src/components/inner-blocks/index.native.js b/packages/block-editor/src/components/inner-blocks/index.native.js new file mode 100644 index 00000000000000..d0515d6d8a58ac --- /dev/null +++ b/packages/block-editor/src/components/inner-blocks/index.native.js @@ -0,0 +1,182 @@ +/** + * External dependencies + */ +import { pick, isEqual } from 'lodash'; + +/** + * WordPress dependencies + */ +import { Component } from '@wordpress/element'; +import { withSelect, withDispatch } from '@wordpress/data'; +import { synchronizeBlocksWithTemplate, withBlockContentContext } from '@wordpress/blocks'; +import isShallowEqual from '@wordpress/is-shallow-equal'; +import { compose } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import ButtonBlockAppender from './button-block-appender'; +import DefaultBlockAppender from './default-block-appender'; + +/** + * Internal dependencies + */ +import BlockList from '../block-list'; +import { withBlockEditContext } from '../block-edit/context'; + +class InnerBlocks extends Component { + constructor() { + super( ...arguments ); + this.state = { + templateInProcess: !! this.props.template, + }; + this.updateNestedSettings(); + } + + getTemplateLock() { + const { + templateLock, + parentLock, + } = this.props; + return templateLock === undefined ? parentLock : templateLock; + } + + componentDidMount() { + const { innerBlocks } = this.props.block; + // only synchronize innerBlocks with template if innerBlocks are empty or a locking all exists + if ( innerBlocks.length === 0 || this.getTemplateLock() === 'all' ) { + this.synchronizeBlocksWithTemplate(); + } + + if ( this.state.templateInProcess ) { + this.setState( { + templateInProcess: false, + } ); + } + } + + componentDidUpdate( prevProps ) { + const { template, block } = this.props; + const { innerBlocks } = block; + + this.updateNestedSettings(); + // only synchronize innerBlocks with template if innerBlocks are empty or a locking all exists + if ( innerBlocks.length === 0 || this.getTemplateLock() === 'all' ) { + const hasTemplateChanged = ! isEqual( template, prevProps.template ); + if ( hasTemplateChanged ) { + this.synchronizeBlocksWithTemplate(); + } + } + } + + /** + * Called on mount or when a mismatch exists between the templates and + * inner blocks, synchronizes inner blocks with the template, replacing + * current blocks. + */ + synchronizeBlocksWithTemplate() { + const { template, block, replaceInnerBlocks } = this.props; + const { innerBlocks } = block; + + // Synchronize with templates. If the next set differs, replace. + const nextBlocks = synchronizeBlocksWithTemplate( innerBlocks, template ); + if ( ! isEqual( nextBlocks, innerBlocks ) ) { + replaceInnerBlocks( nextBlocks ); + } + } + + updateNestedSettings() { + const { + blockListSettings, + allowedBlocks, + updateNestedSettings, + } = this.props; + + const newSettings = { + allowedBlocks, + templateLock: this.getTemplateLock(), + }; + + if ( ! isShallowEqual( blockListSettings, newSettings ) ) { + updateNestedSettings( newSettings ); + } + } + + render() { + const { + clientId, + renderAppender, + template, + __experimentalTemplateOptions: templateOptions, + } = this.props; + const { templateInProcess } = this.state; + + const isPlaceholder = template === null && !! templateOptions; + + return ( + <> + { ! templateInProcess && ( + isPlaceholder ? + null : + <BlockList + rootClientId={ clientId } + renderAppender={ renderAppender } + /> + ) } + </> + ); + } +} + +InnerBlocks = compose( [ + withBlockEditContext( ( context ) => pick( context, [ 'clientId' ] ) ), + withSelect( ( select, ownProps ) => { + const { + isBlockSelected, + hasSelectedInnerBlock, + getBlock, + getBlockListSettings, + getBlockRootClientId, + getTemplateLock, + } = select( 'core/block-editor' ); + const { clientId } = ownProps; + const block = getBlock( clientId ); + const rootClientId = getBlockRootClientId( clientId ); + + return { + block, + blockListSettings: getBlockListSettings( clientId ), + hasOverlay: block.name !== 'core/template' && ! isBlockSelected( clientId ) && ! hasSelectedInnerBlock( clientId, true ), + parentLock: getTemplateLock( rootClientId ), + }; + } ), + withDispatch( ( dispatch, ownProps ) => { + const { + replaceInnerBlocks, + updateBlockListSettings, + } = dispatch( 'core/block-editor' ); + const { block, clientId, templateInsertUpdatesSelection = true } = ownProps; + + return { + replaceInnerBlocks( blocks ) { + replaceInnerBlocks( clientId, blocks, block.innerBlocks.length === 0 && templateInsertUpdatesSelection ); + }, + updateNestedSettings( settings ) { + dispatch( updateBlockListSettings( clientId, settings ) ); + }, + }; + } ), +] )( InnerBlocks ); + +// Expose default appender placeholders as components. +InnerBlocks.DefaultBlockAppender = DefaultBlockAppender; +InnerBlocks.ButtonBlockAppender = ButtonBlockAppender; + +InnerBlocks.Content = withBlockContentContext( + ( { BlockContent } ) => <BlockContent /> +); + +/** + * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/inner-blocks/README.md + */ +export default InnerBlocks; diff --git a/packages/block-editor/src/components/url-input/test/button.js b/packages/block-editor/src/components/url-input/test/button.js index 427ea0ca8531e8..8088d61ea5016e 100644 --- a/packages/block-editor/src/components/url-input/test/button.js +++ b/packages/block-editor/src/components/url-input/test/button.js @@ -63,17 +63,17 @@ describe( 'URLInputButton', () => { } ); it( 'should close the form when user submits it', () => { const wrapper = TestUtils.renderIntoDocument( <URLInputButton /> ); - const buttonElement = () => TestUtils.findRenderedDOMComponentWithClass( + const buttonElement = () => TestUtils.scryRenderedDOMComponentsWithClass( wrapper, 'components-toolbar__control' ); - const formElement = () => TestUtils.findRenderedDOMComponentWithTag( + const formElement = () => TestUtils.scryRenderedDOMComponentsWithTag( wrapper, 'form' ); - TestUtils.Simulate.click( buttonElement() ); + TestUtils.Simulate.click( buttonElement().shift() ); expect( wrapper.state.expanded ).toBe( true ); - TestUtils.Simulate.submit( formElement() ); + TestUtils.Simulate.submit( formElement().shift() ); expect( wrapper.state.expanded ).toBe( false ); // eslint-disable-next-line react/no-find-dom-node ReactDOM.unmountComponentAtNode( ReactDOM.findDOMNode( wrapper ).parentNode ); diff --git a/packages/block-library/src/index.native.js b/packages/block-library/src/index.native.js index b3411411ee75fd..ebfeefe38b9dd1 100644 --- a/packages/block-library/src/index.native.js +++ b/packages/block-library/src/index.native.js @@ -101,6 +101,33 @@ export const coreBlocks = [ return memo; }, {} ); +/** + * Function to register an individual block. + * + * @param {Object} block The block to be registered. + * + */ +const registerBlock = ( block ) => { + if ( ! block ) { + return; + } + const { metadata, settings, name } = block; + registerBlockType( name, { + ...metadata, + ...settings, + } ); +}; + +/** + * Function to register core blocks provided by the block editor. + * + * @example + * ```js + * import { registerCoreBlocks } from '@wordpress/block-library'; + * + * registerCoreBlocks(); + * ``` + */ export const registerCoreBlocks = () => { [ paragraph, @@ -114,13 +141,10 @@ export const registerCoreBlocks = () => { separator, list, quote, - ].forEach( ( { metadata, name, settings } ) => { - registerBlockType( name, { - ...metadata, - ...settings, - } ); - } ); -}; + // eslint-disable-next-line no-undef + typeof __DEV__ !== 'undefined' && __DEV__ ? mediaText : null, + ].forEach( registerBlock ); -setDefaultBlockName( paragraph.name ); -setUnregisteredTypeHandlerName( missing.name ); + setDefaultBlockName( paragraph.name ); + setUnregisteredTypeHandlerName( missing.name ); +}; diff --git a/packages/block-library/src/media-text/edit.native.js b/packages/block-library/src/media-text/edit.native.js new file mode 100644 index 00000000000000..3cfb91b48ce949 --- /dev/null +++ b/packages/block-library/src/media-text/edit.native.js @@ -0,0 +1,186 @@ +/** + * External dependencies + */ +import { get } from 'lodash'; +import { View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { __, _x } from '@wordpress/i18n'; +import { + BlockControls, + BlockVerticalAlignmentToolbar, + InnerBlocks, + withColors, +} from '@wordpress/block-editor'; +import { Component } from '@wordpress/element'; +import { + Toolbar, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import MediaContainer from './media-container'; +import styles from './style.scss'; + +/** + * Constants + */ +const ALLOWED_BLOCKS = [ 'core/button', 'core/paragraph', 'core/heading', 'core/list' ]; +const TEMPLATE = [ + [ 'core/paragraph', { fontSize: 'large', placeholder: _x( 'Content…', 'content placeholder' ) } ], +]; +// this limits the resize to a safe zone to avoid making broken layouts +const WIDTH_CONSTRAINT_PERCENTAGE = 15; +const applyWidthConstraints = ( width ) => Math.max( WIDTH_CONSTRAINT_PERCENTAGE, Math.min( width, 100 - WIDTH_CONSTRAINT_PERCENTAGE ) ); + +class MediaTextEdit extends Component { + constructor() { + super( ...arguments ); + + this.onSelectMedia = this.onSelectMedia.bind( this ); + this.onWidthChange = this.onWidthChange.bind( this ); + this.commitWidthChange = this.commitWidthChange.bind( this ); + this.state = { + mediaWidth: null, + }; + } + + onSelectMedia( media ) { + const { setAttributes } = this.props; + + let mediaType; + let src; + // for media selections originated from a file upload. + if ( media.media_type ) { + if ( media.media_type === 'image' ) { + mediaType = 'image'; + } else { + // only images and videos are accepted so if the media_type is not an image we can assume it is a video. + // video contain the media type of 'file' in the object returned from the rest api. + mediaType = 'video'; + } + } else { // for media selections originated from existing files in the media library. + mediaType = media.type; + } + + if ( mediaType === 'image' ) { + // Try the "large" size URL, falling back to the "full" size URL below. + src = get( media, [ 'sizes', 'large', 'url' ] ) || get( media, [ 'media_details', 'sizes', 'large', 'source_url' ] ); + } + + setAttributes( { + mediaAlt: media.alt, + mediaId: media.id, + mediaType, + mediaUrl: src || media.url, + imageFill: undefined, + focalPoint: undefined, + } ); + } + + onWidthChange( width ) { + this.setState( { + mediaWidth: applyWidthConstraints( width ), + } ); + } + + commitWidthChange( width ) { + const { setAttributes } = this.props; + + setAttributes( { + mediaWidth: applyWidthConstraints( width ), + } ); + this.setState( { + mediaWidth: null, + } ); + } + + renderMediaArea() { + const { attributes } = this.props; + const { mediaAlt, mediaId, mediaPosition, mediaType, mediaUrl, mediaWidth, imageFill, focalPoint } = attributes; + + return ( + <MediaContainer + onSelectMedia={ this.onSelectMedia } + onWidthChange={ this.onWidthChange } + commitWidthChange={ this.commitWidthChange } + onFocus={ this.props.onFocus } + { ...{ mediaAlt, mediaId, mediaType, mediaUrl, mediaPosition, mediaWidth, imageFill, focalPoint } } + /> + ); + } + + render() { + const { + attributes, + backgroundColor, + setAttributes, + } = this.props; + const { + isStackedOnMobile, + mediaPosition, + mediaWidth, + verticalAlignment, + } = attributes; + const temporaryMediaWidth = this.state.mediaWidth || mediaWidth; + const widthString = `${ temporaryMediaWidth }%`; + const containerStyles = { + ...styles[ 'wp-block-media-text' ], + ...styles[ `is-vertically-aligned-${ verticalAlignment }` ], + ...( mediaPosition === 'right' ? styles[ 'has-media-on-the-right' ] : {} ), + ...( isStackedOnMobile ? styles[ 'is-stacked-on-mobile' ] : {} ), + ...( isStackedOnMobile && mediaPosition === 'right' ? styles[ 'is-stacked-on-mobile.has-media-on-the-right' ] : {} ), + backgroundColor: backgroundColor.color, + }; + const innerBlockWidth = 100 - temporaryMediaWidth; + const innerBlockWidthString = `${ innerBlockWidth }%`; + + const toolbarControls = [ { + icon: 'align-pull-left', + title: __( 'Show media on left' ), + isActive: mediaPosition === 'left', + onClick: () => setAttributes( { mediaPosition: 'left' } ), + }, { + icon: 'align-pull-right', + title: __( 'Show media on right' ), + isActive: mediaPosition === 'right', + onClick: () => setAttributes( { mediaPosition: 'right' } ), + } ]; + + const onVerticalAlignmentChange = ( alignment ) => { + setAttributes( { verticalAlignment: alignment } ); + }; + + return ( + <> + <BlockControls> + <Toolbar + controls={ toolbarControls } + /> + <BlockVerticalAlignmentToolbar + onChange={ onVerticalAlignmentChange } + value={ verticalAlignment } + isCollapsed={ false } + /> + </BlockControls> + <View style={ containerStyles }> + <View style={ { width: widthString } }> + { this.renderMediaArea() } + </View> + <View style={ { width: innerBlockWidthString } }> + <InnerBlocks + allowedBlocks={ ALLOWED_BLOCKS } + template={ TEMPLATE } + templateInsertUpdatesSelection={ false } + /> + </View> + </View> + </> + ); + } +} + +export default withColors( 'backgroundColor' )( MediaTextEdit ); diff --git a/packages/block-library/src/media-text/media-container.native.js b/packages/block-library/src/media-text/media-container.native.js new file mode 100644 index 00000000000000..ab9056cf46dc37 --- /dev/null +++ b/packages/block-library/src/media-text/media-container.native.js @@ -0,0 +1,191 @@ +/** + * External dependencies + */ +import { View, Image, ImageBackground } from 'react-native'; + +/** + * WordPress dependencies + */ +import { IconButton, Toolbar, withNotices } from '@wordpress/components'; +import { + BlockControls, + BlockIcon, + MediaPlaceholder, + MEDIA_TYPE_IMAGE, + MediaUpload, +} from '@wordpress/block-editor'; +import { Component } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import icon from './media-container-icon'; + +export function calculatePreferedImageSize( image, container ) { + const maxWidth = container.clientWidth; + const exceedMaxWidth = image.width > maxWidth; + const ratio = image.height / image.width; + const width = exceedMaxWidth ? maxWidth : image.width; + const height = exceedMaxWidth ? maxWidth * ratio : image.height; + return { width, height }; +} + +class MediaContainer extends Component { + constructor() { + super( ...arguments ); + this.onUploadError = this.onUploadError.bind( this ); + this.calculateSize = this.calculateSize.bind( this ); + this.onLayout = this.onLayout.bind( this ); + this.onSelectURL = this.onSelectURL.bind( this ); + + this.state = { + width: 0, + height: 0, + }; + + if ( this.props.mediaUrl ) { + this.onMediaChange(); + } + } + + onUploadError( message ) { + const { noticeOperations } = this.props; + noticeOperations.removeAllNotices(); + noticeOperations.createErrorNotice( message ); + } + + onSelectURL( mediaId, mediaUrl ) { + const { onSelectMedia } = this.props; + + onSelectMedia( { + media_type: 'image', + id: mediaId, + src: mediaUrl, + } ); + } + + renderToolbarEditButton() { + const { mediaId } = this.props; + return ( + <BlockControls> + <Toolbar> + <MediaUpload + onSelectURL={ this.onSelectURL } + mediaType={ MEDIA_TYPE_IMAGE } + value={ mediaId } + render={ ( { open } ) => ( + <IconButton + className="components-toolbar__control" + label={ __( 'Edit media' ) } + icon="edit" + onClick={ open } + /> + ) } + /> + </Toolbar> + </BlockControls> + ); + } + + componentDidUpdate( prevProps ) { + if ( prevProps.mediaUrl !== this.props.mediaUrl ) { + this.onMediaChange(); + } + } + + onMediaChange() { + const mediaType = this.props.mediaType; + if ( mediaType === 'video' ) { + + } else if ( mediaType === 'image' ) { + Image.getSize( this.props.mediaUrl, ( width, height ) => { + this.media = { width, height }; + this.calculateSize(); + } ); + } + } + + calculateSize() { + if ( this.media === undefined || this.container === undefined ) { + return; + } + + const { width, height } = calculatePreferedImageSize( this.media, this.container ); + this.setState( { width, height } ); + } + + onLayout( event ) { + const { width, height } = event.nativeEvent.layout; + this.container = { + clientWidth: width, + clientHeight: height, + }; + this.calculateSize(); + } + + renderImage() { + const { mediaAlt, mediaUrl } = this.props; + + return ( + <View onLayout={ this.onLayout }> + <ImageBackground + accessible={ true } + //disabled={ ! isSelected } + accessibilityLabel={ mediaAlt } + accessibilityHint={ __( 'Double tap and hold to edit' ) } + accessibilityRole={ 'imagebutton' } + style={ { width: this.state.width, height: this.state.height } } + resizeMethod="scale" + source={ { uri: mediaUrl } } + key={ mediaUrl } + > + </ImageBackground> + </View> + ); + } + + renderVideo() { + const style = { videoContainer: {} }; + return ( + <View onLayout={ this.onLayout }> + <View style={ style.videoContainer }> + { /* TODO: show video preview */ } + </View> + </View> + ); + } + + renderPlaceholder() { + return ( + <MediaPlaceholder + icon={ <BlockIcon icon={ icon } /> } + labels={ { + title: __( 'Media area' ), + } } + onSelectURL={ this.onSelectURL } + mediaType={ MEDIA_TYPE_IMAGE } + onFocus={ this.props.onFocus } + /> + ); + } + + render() { + const { mediaUrl, mediaType } = this.props; + if ( mediaType && mediaUrl ) { + let mediaElement = null; + switch ( mediaType ) { + case 'image': + mediaElement = this.renderImage(); + break; + case 'video': + mediaElement = this.renderVideo(); + break; + } + return mediaElement; + } + return this.renderPlaceholder(); + } +} + +export default withNotices( MediaContainer ); diff --git a/packages/block-library/src/media-text/style.native.scss b/packages/block-library/src/media-text/style.native.scss new file mode 100644 index 00000000000000..f1c3550f29c1e9 --- /dev/null +++ b/packages/block-library/src/media-text/style.native.scss @@ -0,0 +1,29 @@ +.wp-block-media-text { + display: flex; + align-items: flex-start; + flex-direction: row; +} + +.has-media-on-the-right { + flex-direction: row-reverse; +} + +.is-stacked-on-mobile { + flex-direction: column; + + &.has-media-on-the-right { + flex-direction: column-reverse; + } +} + +.is-vertically-aligned-top { + align-items: flex-start; +} + +.is-vertically-aligned-center { + align-items: center; +} + +.is-vertically-aligned-bottom { + align-items: flex-end; +} diff --git a/packages/blocks/src/api/index.native.js b/packages/blocks/src/api/index.native.js index 3b3be8f28c3a46..f8d4c03298aebe 100644 --- a/packages/blocks/src/api/index.native.js +++ b/packages/blocks/src/api/index.native.js @@ -35,5 +35,9 @@ export { isUnmodifiedDefaultBlock, normalizeIconObject, } from './utils'; +export { + doBlocksMatchTemplate, + synchronizeBlocksWithTemplate, +} from './templates'; export { pasteHandler, getPhrasingContentSchema } from './raw-handling'; export { default as children } from './children'; diff --git a/packages/components/src/icon-button/index.js b/packages/components/src/icon-button/index.js index f64d7f6777e934..377feab7601cfc 100644 --- a/packages/components/src/icon-button/index.js +++ b/packages/components/src/icon-button/index.js @@ -2,7 +2,7 @@ * External dependencies */ import classnames from 'classnames'; -import { isArray, isString } from 'lodash'; +import { isArray } from 'lodash'; /** * WordPress dependencies @@ -14,7 +14,7 @@ import { forwardRef } from '@wordpress/element'; */ import Tooltip from '../tooltip'; import Button from '../button'; -import Dashicon from '../dashicon'; +import Icon from '../icon'; function IconButton( props, ref ) { const { @@ -56,7 +56,7 @@ function IconButton( props, ref ) { className={ classes } ref={ ref } > - { isString( icon ) ? <Dashicon icon={ icon } ariaPressed={ ariaPressed } /> : icon } + <Icon icon={ icon } ariaPressed={ ariaPressed } /> { children } </Button> ); diff --git a/packages/components/src/icon-button/test/index.js b/packages/components/src/icon-button/test/index.js index e824ed1d556ec9..f4a8d4330d3cc2 100644 --- a/packages/components/src/icon-button/test/index.js +++ b/packages/components/src/icon-button/test/index.js @@ -24,7 +24,7 @@ describe( 'IconButton', () => { it( 'should render a Dashicon component matching the wordpress icon', () => { const iconButton = shallow( <IconButton icon="wordpress" /> ); - expect( iconButton.find( 'Dashicon' ).shallow().hasClass( 'dashicons-wordpress' ) ).toBe( true ); + expect( iconButton.find( 'Icon' ).dive().shallow().hasClass( 'dashicons-wordpress' ) ).toBe( true ); } ); it( 'should render child elements when passed as children', () => { diff --git a/packages/components/src/icon/index.js b/packages/components/src/icon/index.js index 61a4fb0a2d6c47..5e762d1d5c8a98 100644 --- a/packages/components/src/icon/index.js +++ b/packages/components/src/icon/index.js @@ -6,20 +6,26 @@ import { cloneElement, createElement, Component, isValidElement } from '@wordpre /** * Internal dependencies */ -import { Dashicon, SVG } from '../'; +import Dashicon from '../dashicon'; +import { SVG } from '../primitives'; function Icon( { icon = null, size, ...additionalProps } ) { - let iconSize; + // Dashicons should be 20x20 by default. + const dashiconSize = size || 20; if ( 'string' === typeof icon ) { - // Dashicons should be 20x20 by default - iconSize = size || 20; - return <Dashicon icon={ icon } size={ iconSize } { ...additionalProps } />; + return <Dashicon icon={ icon } size={ dashiconSize } { ...additionalProps } />; } - // Any other icons should be 24x24 by default - iconSize = size || 24; + if ( icon && Dashicon === icon.type ) { + return cloneElement( icon, { + size: dashiconSize, + ...additionalProps, + } ); + } + // Icons should be 24x24 by default. + const iconSize = size || 24; if ( 'function' === typeof icon ) { if ( icon.prototype instanceof Component ) { return createElement( icon, { size: iconSize, ...additionalProps } ); diff --git a/packages/components/src/icon/test/index.js b/packages/components/src/icon/test/index.js index 053b0cf390ff58..a645568c1e2eee 100644 --- a/packages/components/src/icon/test/index.js +++ b/packages/components/src/icon/test/index.js @@ -11,6 +11,7 @@ import { Component } from '@wordpress/element'; /** * Internal dependencies */ +import Dashicon from '../../dashicon'; import Icon from '../'; import { Path, SVG } from '../../'; @@ -31,12 +32,18 @@ describe( 'Icon', () => { expect( wrapper.find( 'Dashicon' ).prop( 'icon' ) ).toBe( 'format-image' ); } ); - it( 'renders a dashicon and with a default size of 20', () => { + it( 'renders a dashicon by slug and with a default size of 20', () => { const wrapper = shallow( <Icon icon="format-image" /> ); expect( wrapper.find( 'Dashicon' ).prop( 'size' ) ).toBe( 20 ); } ); + it( 'renders a dashicon by element and with a default size of 20', () => { + const wrapper = shallow( <Icon icon={ <Dashicon icon="format-image" /> } /> ); + + expect( wrapper.find( 'Dashicon' ).prop( 'size' ) ).toBe( 20 ); + } ); + it( 'renders a function', () => { const wrapper = shallow( <Icon icon={ () => <span /> } /> ); @@ -98,6 +105,7 @@ describe( 'Icon', () => { describe.each( [ [ 'dashicon', { icon: 'format-image' } ], + [ 'dashicon element', { icon: <Dashicon icon="format-image" /> } ], [ 'element', { icon: <span /> } ], [ 'svg element', { icon: svg } ], [ 'component', { icon: MyComponent } ], diff --git a/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js b/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js index fb9dab39a03f2b..33c56715268971 100644 --- a/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js +++ b/packages/components/src/mobile/keyboard-aware-flat-list/index.ios.js @@ -8,6 +8,7 @@ export const KeyboardAwareFlatList = ( { extraScrollHeight, shouldPreventAutomaticScroll, innerRef, + autoScroll, ...listProps } ) => ( <KeyboardAwareScrollView @@ -17,6 +18,7 @@ export const KeyboardAwareFlatList = ( { keyboardShouldPersistTaps="handled" extraScrollHeight={ extraScrollHeight } extraHeight={ 0 } + enableAutomaticScroll={ autoScroll === undefined ? false : autoScroll } innerRef={ ( ref ) => { this.scrollViewRef = ref; innerRef( ref ); diff --git a/packages/components/src/primitives/svg/index.native.js b/packages/components/src/primitives/svg/index.native.js index b0272e6b5a7b9f..4ee8dbae9b798d 100644 --- a/packages/components/src/primitives/svg/index.native.js +++ b/packages/components/src/primitives/svg/index.native.js @@ -18,7 +18,9 @@ export { export const SVG = ( props ) => { const stylesFromClasses = ( props.className || '' ).split( ' ' ).map( ( element ) => styles[ element ] ).filter( Boolean ); - const styleValues = Object.assign( {}, props.style, ...stylesFromClasses ); + const stylesFromAriaPressed = props.ariaPressed ? styles[ 'is-active' ] : styles[ 'components-toolbar__control' ]; + const styleValues = Object.assign( {}, props.style, stylesFromAriaPressed, ...stylesFromClasses ); + const safeProps = { ...props, style: styleValues }; return ( diff --git a/packages/components/src/primitives/svg/style.native.scss b/packages/components/src/primitives/svg/style.native.scss index 595372b06329e1..95dd5b9856bd7a 100644 --- a/packages/components/src/primitives/svg/style.native.scss +++ b/packages/components/src/primitives/svg/style.native.scss @@ -1,9 +1,11 @@ -.dashicon { +.dashicon, +.components-toolbar__control { color: #7b9ab1; fill: currentColor; } -.dashicon-active { +.dashicon-active, +.is-active { color: #fff; fill: currentColor; } diff --git a/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap b/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap index 3f01ba28572d48..5096eaa7c803b5 100644 --- a/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap +++ b/packages/edit-post/src/components/header/more-menu/test/__snapshots__/index.js.snap @@ -74,22 +74,16 @@ exports[`MoreMenu should match snapshot 1`] = ` onMouseLeave={[Function]} type="button" > - <Dashicon + <Icon icon="ellipsis" key="0,0" > - <SVG - aria-hidden={true} - className="dashicon dashicons-ellipsis" - focusable="false" - height={20} - role="img" - viewBox="0 0 20 20" - width={20} - xmlns="http://www.w3.org/2000/svg" + <Dashicon + icon="ellipsis" + size={20} > - <svg - aria-hidden="true" + <SVG + aria-hidden={true} className="dashicon dashicons-ellipsis" focusable="false" height={20} @@ -98,16 +92,27 @@ exports[`MoreMenu should match snapshot 1`] = ` width={20} xmlns="http://www.w3.org/2000/svg" > - <Path - d="M5 10c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm12-2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-7 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" + <svg + aria-hidden="true" + className="dashicon dashicons-ellipsis" + focusable="false" + height={20} + role="img" + viewBox="0 0 20 20" + width={20} + xmlns="http://www.w3.org/2000/svg" > - <path + <Path d="M5 10c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm12-2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-7 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" - /> - </Path> - </svg> - </SVG> - </Dashicon> + > + <path + d="M5 10c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm12-2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-7 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" + /> + </Path> + </svg> + </SVG> + </Dashicon> + </Icon> </button> </ForwardRef(Button)> </Tooltip> diff --git a/packages/edit-post/src/components/visual-editor/index.native.js b/packages/edit-post/src/components/visual-editor/index.native.js index 9d1925356a9138..b7c53b12aad545 100644 --- a/packages/edit-post/src/components/visual-editor/index.native.js +++ b/packages/edit-post/src/components/visual-editor/index.native.js @@ -53,6 +53,7 @@ class VisualEditor extends Component { header={ this.renderHeader() } isFullyBordered={ isFullyBordered } safeAreaBottomInset={ safeAreaBottomInset } + autoScroll={ true } /> ); } From 3db95b76b55905c0ed43a81c8ce7695cb52470e3 Mon Sep 17 00:00:00 2001 From: Drapich Piotr <drapich.piotr@gmail.com> Date: Fri, 30 Aug 2019 09:05:11 +0200 Subject: [PATCH 11/31] MediaUpload and MediaPlaceholder unify props (#17145) --- .../components/media-placeholder/README.md | 16 ++- .../media-placeholder/index.native.js | 11 ++- .../src/components/media-upload/README.md | 10 +- .../components/media-upload/index.native.js | 99 ++++++++++--------- .../media-upload/test/index.native.js | 18 ++-- .../block-library/src/image/edit.native.js | 18 ++-- .../block-library/src/video/edit.native.js | 12 +-- 7 files changed, 105 insertions(+), 79 deletions(-) diff --git a/packages/block-editor/src/components/media-placeholder/README.md b/packages/block-editor/src/components/media-placeholder/README.md index 59795b5469c8cc..bde6c7598042ba 100644 --- a/packages/block-editor/src/components/media-placeholder/README.md +++ b/packages/block-editor/src/components/media-placeholder/README.md @@ -37,6 +37,7 @@ This property is similar to the `allowedTypes` property. The difference is the f - Type: `String` - Required: No +- Platform: Web ### addToGallery @@ -46,6 +47,7 @@ If false the gallery media modal opens in the edit mode where the user can edit - Type: `Boolean` - Required: No - Default: `false` +- Platform: Web ### allowedTypes @@ -57,6 +59,7 @@ This property is similar to the `accept` property. The difference is the format - Type: `Array` - Required: No +- Platform: Web | Mobile ### className @@ -64,6 +67,7 @@ Class name added to the placeholder. - Type: `String` - Required: No +- Platform: Web ### icon @@ -71,6 +75,7 @@ Icon to display left of the title. When passed as a `String`, the icon will be r - Type: `String|WPComponent` - Required: No +- Platform: Web | Mobile ### isAppender @@ -80,6 +85,7 @@ If false the default placeholder style is used. - Type: `Boolean` - Required: No - Default: `false` +- Platform: Web ### labels @@ -87,7 +93,7 @@ An object that can contain a `title` and `instructions` properties. These proper - Type: `Object` - Required: No - +- Platform: Web | Mobile ### multiple @@ -96,6 +102,7 @@ Whether to allow multiple selection of files or not. - Type: `Boolean` - Required: No - Default: `false` +- Platform: Web ### onError @@ -103,6 +110,7 @@ Callback called when an upload error happens. - Type: `Function` - Required: No +- Platform: Web ### onSelect @@ -111,6 +119,11 @@ The call back receives an array with the new files. Each element of the collecti - Type: `Function` - Required: Yes +- Platform: Web | Mobile + +The argument of the callback is an object containing the following properties: +- Web: `{ url, alt, id, link, caption, sizes, media_details }` +- Mobile: `{ id, url }` ### value @@ -118,6 +131,7 @@ Media ID (or media IDs if multiple is true) to be selected by default when openi - Type: `Number|Array` - Required: No +- Platform: Web ## Extend diff --git a/packages/block-editor/src/components/media-placeholder/index.native.js b/packages/block-editor/src/components/media-placeholder/index.native.js index e04361b71f3307..e224cce83c45e4 100644 --- a/packages/block-editor/src/components/media-placeholder/index.native.js +++ b/packages/block-editor/src/components/media-placeholder/index.native.js @@ -16,10 +16,11 @@ import { withTheme, useStyle } from '@wordpress/components'; import styles from './styles.scss'; function MediaPlaceholder( props ) { - const { mediaType, labels = {}, icon, onSelectURL, theme } = props; + const { allowedTypes = [], labels = {}, icon, onSelect, theme } = props; - const isImage = MEDIA_TYPE_IMAGE === mediaType; - const isVideo = MEDIA_TYPE_VIDEO === mediaType; + const isOneType = allowedTypes.length === 1; + const isImage = isOneType && allowedTypes.includes( MEDIA_TYPE_IMAGE ); + const isVideo = isOneType && allowedTypes.includes( MEDIA_TYPE_VIDEO ); let placeholderTitle = labels.title; if ( placeholderTitle === undefined ) { @@ -52,8 +53,8 @@ function MediaPlaceholder( props ) { return ( <MediaUpload - mediaType={ mediaType } - onSelectURL={ onSelectURL } + allowedTypes={ allowedTypes } + onSelect={ onSelect } render={ ( { open, getMediaOptions } ) => { return ( <TouchableWithoutFeedback diff --git a/packages/block-editor/src/components/media-upload/README.md b/packages/block-editor/src/components/media-upload/README.md index 6e0a67d9f187c6..afcc4f368a6f13 100644 --- a/packages/block-editor/src/components/media-upload/README.md +++ b/packages/block-editor/src/components/media-upload/README.md @@ -63,6 +63,7 @@ If allowedTypes is unset all mime types should be allowed. - Type: `Array` - Required: No +- Platform: Web | Mobile ### multiple @@ -71,6 +72,7 @@ Whether to allow multiple selections or not. - Type: `Boolean` - Required: No - Default: false +- Platform: Web ### value @@ -78,6 +80,7 @@ Media ID (or media IDs if multiple is true) to be selected by default when openi - Type: `Number|Array` - Required: No +- Platform: Web ### onSelect @@ -85,6 +88,7 @@ Callback called when the media modal is closed, the selected media are passed as - Type: `Function` - Required: Yes +- Platform: Web | Mobile ### title @@ -93,6 +97,7 @@ Title displayed in the media modal. - Type: `String` - Required: No - Default: `Select or Upload Media` +- Platform: Web ### modalClass @@ -100,7 +105,7 @@ CSS class added to the media modal frame. - Type: `String` - Required: No - +- Platform: Web ### addToGallery @@ -111,6 +116,7 @@ Only applies if `gallery === true`. - Type: `Boolean` - Required: No - Default: `false` +- Platform: Web ### gallery @@ -119,6 +125,7 @@ If true, the component will initiate all the states required to represent a gall - Type: `Boolean` - Required: No - Default: `false` +- Platform: Web ## render @@ -126,6 +133,7 @@ A callback invoked to render the Button opening the media library. - Type: `Function` - Required: Yes +- Platform: Web | Mobile The first argument of the callback is an object containing the following properties: 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 14ed5d8dbe6236..88208d961c3d2a 100644 --- a/packages/block-editor/src/components/media-upload/index.native.js +++ b/packages/block-editor/src/components/media-upload/index.native.js @@ -23,16 +23,27 @@ export const MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_WORD_PRESS_LIBRARY = 'wordpress_med 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 class MediaUpload extends React.Component { + constructor( props ) { + super( props ); + this.onPickerPresent = this.onPickerPresent.bind( this ); + this.onPickerChange = this.onPickerChange.bind( this ); + this.onPickerSelect = this.onPickerSelect.bind( this ); + } getTakeMediaLabel() { - const { mediaType } = this.props; + const { allowedTypes = [] } = this.props; + + const isOneType = allowedTypes.length === 1; + const isImage = isOneType && allowedTypes.includes( MEDIA_TYPE_IMAGE ); + const isVideo = isOneType && allowedTypes.includes( MEDIA_TYPE_VIDEO ); - if ( mediaType === MEDIA_TYPE_IMAGE ) { + if ( isImage ) { return OPTION_TAKE_PHOTO; - } else if ( mediaType === MEDIA_TYPE_VIDEO ) { + } else if ( isVideo ) { return OPTION_TAKE_VIDEO; - } + } return OPTION_TAKE_PHOTO_OR_VIDEO; } getMediaOptionsItems() { @@ -44,11 +55,15 @@ export class MediaUpload extends React.Component { } getChooseFromDeviceIcon() { - const { mediaType } = this.props; + const { allowedTypes = [] } = this.props; + + const isOneType = allowedTypes.length === 1; + const isImage = isOneType && allowedTypes.includes( MEDIA_TYPE_IMAGE ); + const isVideo = isOneType && allowedTypes.includes( MEDIA_TYPE_VIDEO ); - if ( mediaType === MEDIA_TYPE_IMAGE ) { + if ( isImage || ! isOneType ) { return 'format-image'; - } else if ( mediaType === MEDIA_TYPE_VIDEO ) { + } else if ( isVideo ) { return 'format-video'; } } @@ -61,58 +76,44 @@ export class MediaUpload extends React.Component { return 'wordpress-alt'; } - render() { - const { mediaType } = this.props; - - const onMediaLibraryButtonPressed = () => { - requestMediaPickFromMediaLibrary( [ mediaType ], ( mediaId, mediaUrl ) => { - if ( mediaId ) { - this.props.onSelectURL( mediaId, mediaUrl ); - } - } ); - }; - - const onMediaUploadButtonPressed = () => { - requestMediaPickFromDeviceLibrary( [ mediaType ], ( mediaId, mediaUrl ) => { - if ( mediaId ) { - this.props.onSelectURL( mediaId, mediaUrl ); - } - } ); - }; - - const onMediaCaptureButtonPressed = () => { - requestMediaPickFromDeviceCamera( [ mediaType ], ( mediaId, mediaUrl ) => { - if ( mediaId ) { - this.props.onSelectURL( mediaId, mediaUrl ); - } - } ); - }; + onPickerPresent() { + if ( this.picker ) { + this.picker.presentPicker(); + } + } - const mediaOptions = this.getMediaOptionsItems(); + onPickerSelect( requestFunction ) { + const { allowedTypes = [], onSelect } = this.props; + requestFunction( allowedTypes, ( id, url ) => { + if ( id ) { + onSelect( { id, url } ); + } + } ); + } - let picker; + onPickerChange( value ) { + if ( value === MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_CHOOSE_FROM_DEVICE ) { + this.onPickerSelect( requestMediaPickFromDeviceLibrary ); + } else if ( value === MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_TAKE_MEDIA ) { + this.onPickerSelect( requestMediaPickFromDeviceCamera ); + } else if ( value === MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_WORD_PRESS_LIBRARY ) { + this.onPickerSelect( requestMediaPickFromMediaLibrary ); + } + } - const onPickerPresent = () => { - picker.presentPicker(); - }; + render() { + const mediaOptions = this.getMediaOptionsItems(); const getMediaOptions = () => ( <Picker hideCancelButton={ true } - ref={ ( instance ) => picker = instance } + ref={ ( instance ) => this.picker = instance } options={ mediaOptions } - onChange={ ( value ) => { - if ( value === MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_CHOOSE_FROM_DEVICE ) { - onMediaUploadButtonPressed(); - } else if ( value === MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_TAKE_MEDIA ) { - onMediaCaptureButtonPressed(); - } else if ( value === MEDIA_UPLOAD_BOTTOM_SHEET_VALUE_WORD_PRESS_LIBRARY ) { - onMediaLibraryButtonPressed(); - } - } } + onChange={ this.onPickerChange } /> ); - return this.props.render( { open: onPickerPresent, getMediaOptions } ); + + return this.props.render( { open: this.onPickerPresent, getMediaOptions } ); } } 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 6eca7575ac408b..d1eec5a560b7b7 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 @@ -29,14 +29,14 @@ const MEDIA_ID = 123; describe( 'MediaUpload component', () => { it( 'renders without crashing', () => { const wrapper = shallow( - <MediaUpload render={ () => {} } /> + <MediaUpload allowedTypes={ [] } render={ () => {} } /> ); expect( wrapper ).toBeTruthy(); } ); it( 'opens media options picker', () => { const wrapper = shallow( - <MediaUpload render={ ( { open, getMediaOptions } ) => { + <MediaUpload allowedTypes={ [] } render={ ( { open, getMediaOptions } ) => { return ( <TouchableWithoutFeedback onPress={ open }> { getMediaOptions() } @@ -51,7 +51,7 @@ describe( 'MediaUpload component', () => { const expectOptionForMediaType = ( mediaType, expectedOption ) => { const wrapper = shallow( <MediaUpload - mediaType={ mediaType } + allowedTypes={ [ mediaType ] } render={ ( { open, getMediaOptions } ) => { return ( <TouchableWithoutFeedback onPress={ open }> @@ -72,12 +72,12 @@ describe( 'MediaUpload component', () => { callback( MEDIA_ID, MEDIA_URL ); } ); - const onSelectURL = jest.fn(); + const onSelect = jest.fn(); const wrapper = shallow( <MediaUpload - mediaType={ MEDIA_TYPE_VIDEO } - onSelectURL={ onSelectURL } + allowedTypes={ [ MEDIA_TYPE_VIDEO ] } + onSelect={ onSelect } render={ ( { open, getMediaOptions } ) => { return ( <TouchableWithoutFeedback onPress={ open }> @@ -87,10 +87,12 @@ describe( 'MediaUpload component', () => { } } /> ); wrapper.find( 'Picker' ).simulate( 'change', option ); + const media = { id: MEDIA_ID, url: MEDIA_URL }; + expect( requestFunction ).toHaveBeenCalledTimes( 1 ); - expect( onSelectURL ).toHaveBeenCalledTimes( 1 ); - expect( onSelectURL ).toHaveBeenCalledWith( MEDIA_ID, MEDIA_URL ); + expect( onSelect ).toHaveBeenCalledTimes( 1 ); + expect( onSelect ).toHaveBeenCalledWith( media ); }; it( 'can select media from device library', () => { diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index a21d4bb72830f5..2d059b04c5ca41 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -84,9 +84,9 @@ class ImageEdit extends React.Component { if ( attributes.id && attributes.url && ! isURL( attributes.url ) ) { if ( attributes.url.indexOf( 'file:' ) === 0 ) { - requestMediaImport( attributes.url, ( mediaId, mediaUri ) => { - if ( mediaUri ) { - setAttributes( { url: mediaUri, id: mediaId } ); + requestMediaImport( attributes.url, ( id, url ) => { + if ( url ) { + setAttributes( { id, url } ); } } ); } @@ -178,9 +178,9 @@ class ImageEdit extends React.Component { } ); } - onSelectMediaUploadOption( mediaId, mediaUrl ) { + onSelectMediaUploadOption( { id, url } ) { const { setAttributes } = this.props; - setAttributes( { url: mediaUrl, id: mediaId } ); + setAttributes( { id, url } ); } onFocusCaption() { @@ -265,8 +265,8 @@ class ImageEdit extends React.Component { return ( <View style={ { flex: 1 } } > <MediaPlaceholder - mediaType={ MEDIA_TYPE_IMAGE } - onSelectURL={ this.onSelectMediaUploadOption } + allowedTypes={ [ MEDIA_TYPE_IMAGE ] } + onSelect={ this.onSelectMediaUploadOption } icon={ this.getIcon( false ) } onFocus={ this.props.onFocus } /> @@ -363,8 +363,8 @@ class ImageEdit extends React.Component { ); return ( - <MediaUpload mediaType={ MEDIA_TYPE_IMAGE } - onSelectURL={ this.onSelectMediaUploadOption } + <MediaUpload allowedTypes={ [ MEDIA_TYPE_IMAGE ] } + onSelect={ this.onSelectMediaUploadOption } render={ ( { open, getMediaOptions } ) => { return getImageComponent( open, getMediaOptions ); } } diff --git a/packages/block-library/src/video/edit.native.js b/packages/block-library/src/video/edit.native.js index 9d004832d37f69..4b803ab9d97a95 100644 --- a/packages/block-library/src/video/edit.native.js +++ b/packages/block-library/src/video/edit.native.js @@ -136,9 +136,9 @@ class VideoEdit extends React.Component { this.setState( { isUploadInProgress: false } ); } - onSelectMediaUploadOption( mediaId, mediaUrl ) { + onSelectMediaUploadOption( { id, url } ) { const { setAttributes } = this.props; - setAttributes( { id: mediaId, src: mediaUrl } ); + setAttributes( { id, src: url } ); } onVideoContanerLayout( event ) { @@ -165,8 +165,8 @@ class VideoEdit extends React.Component { const { videoContainerHeight } = this.state; const toolbarEditButton = ( - <MediaUpload mediaType={ MEDIA_TYPE_VIDEO } - onSelectURL={ this.onSelectMediaUploadOption } + <MediaUpload allowedTypes={ [ MEDIA_TYPE_VIDEO ] } + onSelect={ this.onSelectMediaUploadOption } render={ ( { open, getMediaOptions } ) => { return ( <Toolbar> @@ -186,8 +186,8 @@ class VideoEdit extends React.Component { return ( <View style={ { flex: 1 } } > <MediaPlaceholder - mediaType={ MEDIA_TYPE_VIDEO } - onSelectURL={ this.onSelectMediaUploadOption } + allowedTypes={ [ MEDIA_TYPE_VIDEO ] } + onSelect={ this.onSelectMediaUploadOption } icon={ this.getIcon( false, true ) } onFocus={ this.props.onFocus } /> From 7aa44a28a269ce0b350c5b0513de25670c292397 Mon Sep 17 00:00:00 2001 From: Luke Walczak <lukasz.walczak.pwr@gmail.com> Date: Fri, 30 Aug 2019 11:03:46 +0200 Subject: [PATCH 12/31] Unify media placeholder and upload props within media-text (#17268) --- .../src/media-text/media-container.native.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/block-library/src/media-text/media-container.native.js b/packages/block-library/src/media-text/media-container.native.js index ab9056cf46dc37..0b47812e5f5ac0 100644 --- a/packages/block-library/src/media-text/media-container.native.js +++ b/packages/block-library/src/media-text/media-container.native.js @@ -37,7 +37,7 @@ class MediaContainer extends Component { this.onUploadError = this.onUploadError.bind( this ); this.calculateSize = this.calculateSize.bind( this ); this.onLayout = this.onLayout.bind( this ); - this.onSelectURL = this.onSelectURL.bind( this ); + this.onSelectMediaUploadOption = this.onSelectMediaUploadOption.bind( this ); this.state = { width: 0, @@ -55,13 +55,13 @@ class MediaContainer extends Component { noticeOperations.createErrorNotice( message ); } - onSelectURL( mediaId, mediaUrl ) { + onSelectMediaUploadOption( { id, url } ) { const { onSelectMedia } = this.props; onSelectMedia( { media_type: 'image', - id: mediaId, - src: mediaUrl, + id, + url, } ); } @@ -71,8 +71,8 @@ class MediaContainer extends Component { <BlockControls> <Toolbar> <MediaUpload - onSelectURL={ this.onSelectURL } - mediaType={ MEDIA_TYPE_IMAGE } + onSelect={ this.onSelectMediaUploadOption } + allowedTypes={ [ MEDIA_TYPE_IMAGE ] } value={ mediaId } render={ ( { open } ) => ( <IconButton @@ -163,8 +163,8 @@ class MediaContainer extends Component { labels={ { title: __( 'Media area' ), } } - onSelectURL={ this.onSelectURL } - mediaType={ MEDIA_TYPE_IMAGE } + onSelect={ this.onSelectMediaUploadOption } + allowedTypes={ [ MEDIA_TYPE_IMAGE ] } onFocus={ this.props.onFocus } /> ); From f9fa455452e9c49eb102d7d5d94b275f622bc905 Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco <gerardo.sicart@gmail.com> Date: Fri, 30 Aug 2019 11:06:27 +0200 Subject: [PATCH 13/31] [RNMobile] Fix dismiss keyboard button for the post title (#17260) --- .../header/header-toolbar/index.native.js | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/edit-post/src/components/header/header-toolbar/index.native.js b/packages/edit-post/src/components/header/header-toolbar/index.native.js index 97ca3a62349bc5..278b0af30548e1 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.native.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.native.js @@ -30,8 +30,8 @@ function HeaderToolbar( { undo, showInserter, showKeyboardHideButton, - clearSelectedBlock, theme, + onHideKeyboard, } ) { const scrollViewRef = useRef( null ); const scrollToStart = () => { @@ -76,7 +76,7 @@ function HeaderToolbar( { <ToolbarButton title={ __( 'Hide keyboard' ) } icon="keyboard-hide" - onClick={ clearSelectedBlock } + onClick={ onHideKeyboard } extraProps={ { hint: __( 'Tap to hide the keyboard' ) } } /> </Toolbar> @@ -94,11 +94,19 @@ export default compose( [ showInserter: select( 'core/edit-post' ).getEditorMode() === 'visual' && select( 'core/editor' ).getEditorSettings().richEditingEnabled, isTextModeEnabled: select( 'core/edit-post' ).getEditorMode() === 'text', } ) ), - withDispatch( ( dispatch ) => ( { - redo: dispatch( 'core/editor' ).redo, - undo: dispatch( 'core/editor' ).undo, - clearSelectedBlock: dispatch( 'core/block-editor' ).clearSelectedBlock, - } ) ), + withDispatch( ( dispatch ) => { + const { clearSelectedBlock } = dispatch( 'core/block-editor' ); + const { togglePostTitleSelection } = dispatch( 'core/editor' ); + + return { + redo: dispatch( 'core/editor' ).redo, + undo: dispatch( 'core/editor' ).undo, + onHideKeyboard() { + clearSelectedBlock(); + togglePostTitleSelection( false ); + }, + }; + } ), withViewportMatch( { isLargeViewport: 'medium' } ), withTheme, ] )( HeaderToolbar ); From 7b12673e59097506bb11c99dc2f844b3adbe7a3e Mon Sep 17 00:00:00 2001 From: etoledom <etoledom@icloud.com> Date: Fri, 30 Aug 2019 18:09:31 +0200 Subject: [PATCH 14/31] Recover border colors (#17269) --- .../block-editor/src/components/block-list/block.native.js | 2 +- .../block-editor/src/components/block-list/index.native.js | 2 +- packages/edit-post/src/components/layout/index.native.js | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/block-list/block.native.js b/packages/block-editor/src/components/block-list/block.native.js index b87f95445e11f2..69503bf7151e24 100644 --- a/packages/block-editor/src/components/block-list/block.native.js +++ b/packages/block-editor/src/components/block-list/block.native.js @@ -131,7 +131,7 @@ class BlockListBlock extends Component { > { isValid && this.getBlockForType() } { ! isValid && - <BlockInvalidWarning blockTitle={ title } icon={ icon } /> + <BlockInvalidWarning blockTitle={ title } icon={ icon } /> } </View> { isSelected && <BlockMobileToolbar clientId={ clientId } /> } 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 7c67de71c342f0..5a78753c1249bc 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -140,7 +140,7 @@ export class BlockList extends Component { rootClientId={ this.props.rootClientId } onCaretVerticalPositionChange={ this.onCaretVerticalPositionChange } borderStyle={ this.blockHolderBorderStyle() } - focusedBorderColor={ blockHolderFocusedStyle } + focusedBorderColor={ blockHolderFocusedStyle.borderColor } /> ) } </ReadableContentView> ); diff --git a/packages/edit-post/src/components/layout/index.native.js b/packages/edit-post/src/components/layout/index.native.js index 5b6813067211d2..5a10ed14cb9a56 100644 --- a/packages/edit-post/src/components/layout/index.native.js +++ b/packages/edit-post/src/components/layout/index.native.js @@ -118,8 +118,7 @@ class Layout extends Component { <View style={ useStyle( styles.background, styles.backgroundDark, this.props.theme ) }> { isHtmlView ? this.renderHTML() : this.renderVisual() } </View> - <View style={ { flex: 0, flexBasis: marginBottom, height: marginBottom } }> - </View> + <View style={ { flex: 0, flexBasis: marginBottom, height: marginBottom } } /> { ! isHtmlView && ( <KeyboardAvoidingView parentHeight={ this.state.rootViewHeight } From 14d482bbf2414a521cd2138c369c001f1b529483 Mon Sep 17 00:00:00 2001 From: Matt Chowning <matt.chowning@automattic.com> Date: Tue, 6 Aug 2019 17:04:35 -0400 Subject: [PATCH 15/31] [RNMobile] Insure tapping at end of post inserts at end Previously, tapping at the end of the post would insert a block immediately after the currently selected block. In addition, this commit is cleaning out a few unusued props in the block-list file. --- .../src/components/block-list/index.native.js | 32 +++---------------- 1 file changed, 4 insertions(+), 28 deletions(-) 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 5a78753c1249bc..a2e7fbf9511479 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -33,31 +33,12 @@ export class BlockList extends Component { this.renderDefaultBlockAppender = this.renderDefaultBlockAppender.bind( this ); this.onCaretVerticalPositionChange = this.onCaretVerticalPositionChange.bind( this ); this.scrollViewInnerRef = this.scrollViewInnerRef.bind( this ); - this.getNewBlockInsertionIndex = this.getNewBlockInsertionIndex.bind( this ); + this.addBlockToEndOfPost = this.addBlockToEndOfPost.bind( this ); this.shouldFlatListPreventAutomaticScroll = this.shouldFlatListPreventAutomaticScroll.bind( this ); } - finishBlockAppendingOrReplacing( newBlock ) { - // now determine whether we need to replace the currently selected block (if it's empty) - // or just add a new block as usual - if ( this.isReplaceable( this.props.selectedBlock ) ) { - // do replace here - this.props.replaceBlock( this.props.selectedBlockClientId, newBlock ); - } else { - this.props.insertBlock( newBlock, this.getNewBlockInsertionIndex() ); - } - } - - getNewBlockInsertionIndex() { - if ( this.props.isPostTitleSelected ) { - // if post title selected, insert at top of post - return 0; - } else if ( this.props.selectedBlockIndex === -1 ) { - // if no block selected, insert at end of post - return this.props.blockCount; - } - // insert after selected block - return this.props.selectedBlockIndex + 1; + addBlockToEndOfPost( newBlock ) { + this.props.insertBlock( newBlock, this.props.blockCount ); } blockHolderBorderStyle() { @@ -162,7 +143,7 @@ export class BlockList extends Component { const paragraphBlock = createBlock( 'core/paragraph' ); return ( <TouchableWithoutFeedback onPress={ () => { - this.finishBlockAppendingOrReplacing( paragraphBlock ); + this.addBlockToEndOfPost( paragraphBlock ); } } > <View style={ styles.blockListFooter } /> </TouchableWithoutFeedback> @@ -174,10 +155,8 @@ export default compose( [ withSelect( ( select, { rootClientId } ) => { const { getBlockCount, - getBlockName, getBlockIndex, getBlockOrder, - getSelectedBlock, getSelectedBlockClientId, getBlockInsertionPoint, isBlockInsertionPointVisible, @@ -210,13 +189,10 @@ export default compose( [ return { blockClientIds, blockCount: getBlockCount( rootClientId ), - getBlockName, isBlockInsertionPointVisible: isBlockInsertionPointVisible(), shouldShowBlockAtIndex, shouldShowInsertionPoint, - selectedBlock: getSelectedBlock(), selectedBlockClientId, - selectedBlockIndex, }; } ), withDispatch( ( dispatch ) => { From 89664ebce2a16230e68efd66609bc3fc67d22b2a Mon Sep 17 00:00:00 2001 From: Luke Walczak <lukasz.walczak.pwr@gmail.com> Date: Tue, 3 Sep 2019 12:18:11 +0200 Subject: [PATCH 16/31] Support group block on mobile (#17251) * First working version of the MediaText component for native mobile * Fix adding a block to an innerblock list * Disable mediaText on production * MediaText native: improve editor visuals * Move BlockToolbar from BlockList to Layout * Remove BlockEditorProvider from BlockList and add native version of EditorProvider to Editor. Plus support InsertionPoint and BlockListAppender * Update BlockMover for native to hide if locked or if it's the only block * Make the vertical align button work, add more styling options for toolbar buttons * Make sure registerCoreBlocks does not break in production * Copy docblock comment from the web version for registerCoreBlocks * Fix focusing on the media placeholder * Only support adding image for now * Update usage of MediaPlaceholder in MediaContainer * Enable autoScroll for just the out most block list * Fix JS Unit tests * Roll back to IconButton refactor and fix tests * Fix BlockVerticalAlignmentToolbar buttons style on mobile * Fix thing for web and ensure ariaPressed is always passed down * Use AriaPressed directly to style SVG on mobile * Update snapshots * Support group block on mobile * Extend shouldShowInsertionPoint condition to be false when group is selected * Code refactor * Update package-lock --- .../block-list-appender/index.native.js | 7 +++ .../src/components/block-list/index.native.js | 36 ++++++++----- .../button-block-appender/index.native.js | 48 +++++++++++++++++ .../button-block-appender/styles.native.scss | 17 +++++++ .../components/inner-blocks/index.native.js | 1 + .../block-library/src/group/edit.native.js | 51 +++++++++++++++++++ .../src/group/editor.native.scss | 6 +++ packages/block-library/src/index.native.js | 5 +- .../components/src/button/index.native.js | 5 +- packages/components/src/index.native.js | 1 + 10 files changed, 161 insertions(+), 16 deletions(-) create mode 100644 packages/block-editor/src/components/button-block-appender/index.native.js create mode 100644 packages/block-editor/src/components/button-block-appender/styles.native.scss create mode 100644 packages/block-library/src/group/edit.native.js create mode 100644 packages/block-library/src/group/editor.native.scss diff --git a/packages/block-editor/src/components/block-list-appender/index.native.js b/packages/block-editor/src/components/block-list-appender/index.native.js index 9a6a24c5575172..6d8fb4e6cee976 100644 --- a/packages/block-editor/src/components/block-list-appender/index.native.js +++ b/packages/block-editor/src/components/block-list-appender/index.native.js @@ -20,11 +20,18 @@ function BlockListAppender( { rootClientId, canInsertDefaultBlock, isLocked, + renderAppender: CustomAppender, } ) { if ( isLocked ) { return null; } + if ( CustomAppender ) { + return ( + <CustomAppender /> + ); + } + if ( canInsertDefaultBlock ) { return ( <DefaultBlockAppender 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 a2e7fbf9511479..2dcf6b487a18f7 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -19,7 +19,7 @@ import { KeyboardAwareFlatList, ReadableContentView, useStyle, withTheme } from */ import styles from './style.scss'; import BlockListBlock from './block'; -import DefaultBlockAppender from '../default-block-appender'; +import BlockListAppender from '../block-list-appender'; const innerToolbarHeight = 44; @@ -60,23 +60,21 @@ export class BlockList extends Component { renderDefaultBlockAppender() { return ( <ReadableContentView> - <DefaultBlockAppender + <BlockListAppender rootClientId={ this.props.rootClientId } - containerStyle={ [ - styles.blockContainerFocused, - this.blockHolderBorderStyle(), - { borderColor: 'transparent' }, - ] } + renderAppender={ this.props.renderAppender } /> </ReadableContentView> ); } render() { + const { clearSelectedBlock, blockClientIds, isFullyBordered, title, header, withFooter = true, renderAppender } = this.props; + return ( <View style={ { flex: 1 } } - onAccessibilityEscape={ this.props.clearSelectedBlock } + onAccessibilityEscape={ clearSelectedBlock } > <KeyboardAwareFlatList { ...( Platform.OS === 'android' ? { removeClippedSubviews: false } : {} ) } // Disable clipping on Android to fix focus losing. See https://github.com/wordpress-mobile/gutenberg-mobile/pull/741#issuecomment-472746541 @@ -86,16 +84,23 @@ export class BlockList extends Component { extraScrollHeight={ innerToolbarHeight + 10 } keyboardShouldPersistTaps="always" style={ useStyle( styles.list, styles.listDark, this.context ) } - data={ this.props.blockClientIds } - extraData={ [ this.props.isFullyBordered ] } + data={ blockClientIds } + extraData={ [ isFullyBordered ] } keyExtractor={ identity } renderItem={ this.renderItem } shouldPreventAutomaticScroll={ this.shouldFlatListPreventAutomaticScroll } - title={ this.props.title } - ListHeaderComponent={ this.props.header } + title={ title } + ListHeaderComponent={ header } ListEmptyComponent={ this.renderDefaultBlockAppender } - ListFooterComponent={ this.renderBlockListFooter } + ListFooterComponent={ withFooter && this.renderBlockListFooter } /> + + { renderAppender && blockClientIds.length > 0 && + <BlockListAppender + rootClientId={ this.props.rootClientId } + renderAppender={ this.props.renderAppender } + /> + } </View> ); } @@ -160,12 +165,15 @@ export default compose( [ getSelectedBlockClientId, getBlockInsertionPoint, isBlockInsertionPointVisible, + getSelectedBlock, } = select( 'core/block-editor' ); const selectedBlockClientId = getSelectedBlockClientId(); const blockClientIds = getBlockOrder( rootClientId ); const insertionPoint = getBlockInsertionPoint(); const blockInsertionPointIsVisible = isBlockInsertionPointVisible(); + const selectedBlock = getSelectedBlock(); + const isSelectedGroup = selectedBlock && selectedBlock.name === 'core/group'; const shouldShowInsertionPoint = ( clientId ) => { return ( blockInsertionPointIsVisible && @@ -177,7 +185,7 @@ export default compose( [ const selectedBlockIndex = getBlockIndex( selectedBlockClientId ); const shouldShowBlockAtIndex = ( index ) => { const shouldHideBlockAtIndex = ( - blockInsertionPointIsVisible && + ! isSelectedGroup && blockInsertionPointIsVisible && // if `index` === `insertionPoint.index`, then block is replaceable index === insertionPoint.index && // only hide selected block diff --git a/packages/block-editor/src/components/button-block-appender/index.native.js b/packages/block-editor/src/components/button-block-appender/index.native.js new file mode 100644 index 00000000000000..b01d3b65109856 --- /dev/null +++ b/packages/block-editor/src/components/button-block-appender/index.native.js @@ -0,0 +1,48 @@ +/** + * External dependencies + */ +import { View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { Button, Dashicon } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import Inserter from '../inserter'; +import styles from './styles.scss'; + +function ButtonBlockAppender( { rootClientId } ) { + return ( + <> + <Inserter + rootClientId={ rootClientId } + renderToggle={ ( { onToggle, disabled, isOpen } ) => ( + <Button + onClick={ onToggle } + aria-expanded={ isOpen } + disabled={ disabled } + fixedRatio={ false } + > + <View style={ [ styles.appender, { flex: 1 } ] }> + <Dashicon + icon="plus-alt" + style={ styles.addBlockButton } + color={ styles.addBlockButton.color } + size={ styles.addBlockButton.size } + /> + </View> + </Button> + ) } + isAppender + /> + </> + ); +} + +/** + * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/button-block-appender/README.md + */ +export default ButtonBlockAppender; diff --git a/packages/block-editor/src/components/button-block-appender/styles.native.scss b/packages/block-editor/src/components/button-block-appender/styles.native.scss new file mode 100644 index 00000000000000..0fd4cf46274e02 --- /dev/null +++ b/packages/block-editor/src/components/button-block-appender/styles.native.scss @@ -0,0 +1,17 @@ +.appender { + align-items: center; + justify-content: center; + background-color: $gray-light; + padding: 12px; + background-color: $white; + border: $border-width solid $light-gray-500; + border-radius: 4px; +} + +.addBlockButton { + color: $white; + background-color: $gray; + border-radius: $icon-button-size-small / 2; + overflow: hidden; + size: $icon-button-size-small; +} diff --git a/packages/block-editor/src/components/inner-blocks/index.native.js b/packages/block-editor/src/components/inner-blocks/index.native.js index d0515d6d8a58ac..2b6a6c9320c923 100644 --- a/packages/block-editor/src/components/inner-blocks/index.native.js +++ b/packages/block-editor/src/components/inner-blocks/index.native.js @@ -121,6 +121,7 @@ class InnerBlocks extends Component { <BlockList rootClientId={ clientId } renderAppender={ renderAppender } + withFooter={ false } /> ) } </> diff --git a/packages/block-library/src/group/edit.native.js b/packages/block-library/src/group/edit.native.js new file mode 100644 index 00000000000000..32c87e3863c881 --- /dev/null +++ b/packages/block-library/src/group/edit.native.js @@ -0,0 +1,51 @@ + +/** + * External dependencies + */ +import { View } from 'react-native'; + +/** + * WordPress dependencies + */ +import { withSelect } from '@wordpress/data'; +import { compose } from '@wordpress/compose'; +import { + InnerBlocks, + withColors, +} from '@wordpress/block-editor'; +/** + * Internal dependencies + */ +import styles from './editor.scss'; + +function GroupEdit( { + hasInnerBlocks, + isSelected, +} ) { + if ( ! isSelected && ! hasInnerBlocks ) { + return ( + <View style={ styles.groupPlaceholder } /> + ); + } + + return ( + <InnerBlocks + renderAppender={ isSelected && InnerBlocks.ButtonBlockAppender } + /> + ); +} + +export default compose( [ + withColors( 'backgroundColor' ), + withSelect( ( select, { clientId } ) => { + const { + getBlock, + } = select( 'core/block-editor' ); + + const block = getBlock( clientId ); + + return { + hasInnerBlocks: !! ( block && block.innerBlocks.length ), + }; + } ), +] )( GroupEdit ); diff --git a/packages/block-library/src/group/editor.native.scss b/packages/block-library/src/group/editor.native.scss new file mode 100644 index 00000000000000..5edfa582287ef5 --- /dev/null +++ b/packages/block-library/src/group/editor.native.scss @@ -0,0 +1,6 @@ +.groupPlaceholder { + padding: 12px; + background-color: $white; + border: $border-width dashed $light-gray-500; + border-radius: 4px; +} diff --git a/packages/block-library/src/index.native.js b/packages/block-library/src/index.native.js index ebfeefe38b9dd1..89635d7218b847 100644 --- a/packages/block-library/src/index.native.js +++ b/packages/block-library/src/index.native.js @@ -49,6 +49,7 @@ import * as textColumns from './text-columns'; import * as verse from './verse'; import * as video from './video'; import * as tagCloud from './tag-cloud'; +import * as group from './group'; export const coreBlocks = [ // Common blocks are grouped at the top to prioritize their display @@ -142,7 +143,9 @@ export const registerCoreBlocks = () => { list, quote, // eslint-disable-next-line no-undef - typeof __DEV__ !== 'undefined' && __DEV__ ? mediaText : null, + !! __DEV__ ? mediaText : null, + // eslint-disable-next-line no-undef + !! __DEV__ ? group : null, ].forEach( registerBlock ); setDefaultBlockName( paragraph.name ); diff --git a/packages/components/src/button/index.native.js b/packages/components/src/button/index.native.js index 78d10226e26def..f7fdd94d13f85d 100644 --- a/packages/components/src/button/index.native.js +++ b/packages/components/src/button/index.native.js @@ -19,6 +19,8 @@ const styles = StyleSheet.create( { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', + }, + fixedRatio: { aspectRatio: 1, }, buttonActive: { @@ -28,7 +30,6 @@ const styles = StyleSheet.create( { alignItems: 'center', borderRadius: 6, borderColor: '#2e4453', - aspectRatio: 1, backgroundColor: '#2e4453', }, subscriptInactive: { @@ -55,6 +56,7 @@ export default function Button( props ) { onClick, disabled, hint, + fixedRatio = true, 'aria-disabled': ariaDisabled, 'aria-label': ariaLabel, 'aria-pressed': ariaPressed, @@ -65,6 +67,7 @@ export default function Button( props ) { const buttonViewStyle = { opacity: isDisabled ? 0.2 : 1, + ...( fixedRatio && styles.fixedRatio ), ...( ariaPressed ? styles.buttonActive : styles.buttonInactive ), }; diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index d09b157160003c..209507ec96b440 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -10,6 +10,7 @@ export { default as Spinner } from './spinner'; export { createSlotFill, Slot, Fill, Provider as SlotFillProvider } from './slot-fill'; export { default as BaseControl } from './base-control'; export { default as TextareaControl } from './textarea-control'; +export { default as Button } from './button'; // Higher-Order Components export { default as withConstrainedTabbing } from './higher-order/with-constrained-tabbing'; From 97df6948f422ccacfd4997b0587907f41a89d0ae Mon Sep 17 00:00:00 2001 From: Cameron Voell <cameronvoell@gmail.com> Date: Tue, 3 Sep 2019 20:05:57 -0700 Subject: [PATCH 17/31] Hide fullscreen feature behind dev flag. Fix lint error. --- packages/block-library/src/image/edit.native.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index 0f8963470048a0..908ca83b5c57ed 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -119,10 +119,11 @@ class ImageEdit extends React.Component { } else if ( attributes.id && ! isURL( attributes.url ) ) { requestImageFailedRetryDialog( attributes.id ); } - + // eslint-disable-next-line no-undef + const enableFullscreen = __DEV__; this.setState( { isCaptionSelected: false, - showImageViewer: true, + showImageViewer: enableFullscreen && true, } ); } @@ -272,7 +273,7 @@ class ImageEdit extends React.Component { <ImageViewer isVisible={ this.state.showImageViewer } onClose={ onImageViewerClose } - url ={ url } + url={ url } /> ); From fc8c3dabc14455c025594f0692fe1df3038c0987 Mon Sep 17 00:00:00 2001 From: Luke Walczak <lukasz.walczak.pwr@gmail.com> Date: Wed, 4 Sep 2019 14:02:20 +0200 Subject: [PATCH 18/31] Remove redundant bg color within button appender (#17325) --- .../src/components/button-block-appender/styles.native.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-editor/src/components/button-block-appender/styles.native.scss b/packages/block-editor/src/components/button-block-appender/styles.native.scss index 0fd4cf46274e02..3a6549980af87d 100644 --- a/packages/block-editor/src/components/button-block-appender/styles.native.scss +++ b/packages/block-editor/src/components/button-block-appender/styles.native.scss @@ -1,7 +1,6 @@ .appender { align-items: center; justify-content: center; - background-color: $gray-light; padding: 12px; background-color: $white; border: $border-width solid $light-gray-500; From 264b178ef9bd94d4e0539c1271fb92b5b4490efa Mon Sep 17 00:00:00 2001 From: etoledom <etoledom@icloud.com> Date: Wed, 4 Sep 2019 14:03:38 +0200 Subject: [PATCH 19/31] [RNMobile] DarkMode improvements (#17309) * Remove the need to import `useStyle` and pass the theme prop on every instance that `withStyle` is used * Implement dark-mode refactor on all components * Fix broken native tests * Fix default block appender background color on DarkMode * DarkMode: Make `useStyle` a class function --- .../block-list-appender/style.native.scss | 1 - .../src/components/block-list/index.native.js | 10 +++---- .../components/block-list/style.native.scss | 4 --- .../src/components/inserter/index.native.js | 6 ++-- .../src/components/inserter/menu.native.js | 9 +++--- .../media-placeholder/index.native.js | 8 ++--- .../src/components/warning/index.native.js | 10 +++---- .../block-library/src/code/edit.native.js | 8 ++--- .../block-library/src/image/edit.native.js | 4 +-- .../block-library/src/missing/edit.native.js | 10 +++---- .../block-library/src/more/edit.native.js | 10 ++++--- .../block-library/src/nextpage/edit.native.js | 8 ++--- .../block-library/src/video/edit.native.js | 4 +-- .../src/mobile/bottom-sheet/cell.native.js | 17 +++++------ .../src/mobile/bottom-sheet/index.native.js | 6 ++-- .../src/mobile/dark-mode/index.native.js | 29 +++++++++++-------- .../mobile/html-text-input/index.native.js | 7 +++-- .../html-text-input/test/index.native.js | 9 +++++- .../src/toolbar/toolbar-container.native.js | 8 ++--- .../header/header-toolbar/index.native.js | 6 ++-- .../src/components/layout/index.native.js | 7 +++-- .../components/visual-editor/index.native.js | 6 ++-- .../rich-text/src/component/index.native.js | 10 +++---- .../src/component/test/index.native.js | 5 ++++ 24 files changed, 107 insertions(+), 95 deletions(-) diff --git a/packages/block-editor/src/components/block-list-appender/style.native.scss b/packages/block-editor/src/components/block-list-appender/style.native.scss index 60734f5e9c2106..edb2c6f809ebcc 100644 --- a/packages/block-editor/src/components/block-list-appender/style.native.scss +++ b/packages/block-editor/src/components/block-list-appender/style.native.scss @@ -1,6 +1,5 @@ .blockListAppender { - background-color: $white; padding-left: 16; padding-right: 16; padding-top: 12; 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 2dcf6b487a18f7..54b6b62f08c96b 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -12,7 +12,7 @@ import { __ } from '@wordpress/i18n'; import { withDispatch, withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import { createBlock, isUnmodifiedDefaultBlock } from '@wordpress/blocks'; -import { KeyboardAwareFlatList, ReadableContentView, useStyle, withTheme } from '@wordpress/components'; +import { KeyboardAwareFlatList, ReadableContentView, withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -83,7 +83,7 @@ export class BlockList extends Component { innerRef={ this.scrollViewInnerRef } extraScrollHeight={ innerToolbarHeight + 10 } keyboardShouldPersistTaps="always" - style={ useStyle( styles.list, styles.listDark, this.context ) } + style={ styles.list } data={ blockClientIds } extraData={ [ isFullyBordered ] } keyExtractor={ identity } @@ -113,7 +113,7 @@ export class BlockList extends Component { } renderItem( { item: clientId, index } ) { - const blockHolderFocusedStyle = useStyle( styles.blockHolderFocused, styles.blockHolderFocusedDark, this.props.theme ); + const blockHolderFocusedStyle = this.props.useStyle( styles.blockHolderFocused, styles.blockHolderFocusedDark ); const { shouldShowBlockAtIndex, shouldShowInsertionPoint } = this.props; return ( <ReadableContentView> @@ -133,8 +133,8 @@ export class BlockList extends Component { } renderAddBlockSeparator() { - const lineStyle = useStyle( styles.lineStyleAddHere, styles.lineStyleAddHereDark, this.props.theme ); - const labelStyle = useStyle( styles.labelStyleAddHere, styles.labelStyleAddHereDark, this.props.theme ); + const lineStyle = this.props.useStyle( styles.lineStyleAddHere, styles.lineStyleAddHereDark ); + const labelStyle = this.props.useStyle( styles.labelStyleAddHere, styles.labelStyleAddHereDark ); return ( <View style={ styles.containerStyleAddHere } > <View style={ lineStyle }></View> diff --git a/packages/block-editor/src/components/block-list/style.native.scss b/packages/block-editor/src/components/block-list/style.native.scss index e1f8e96abc948f..36be89c4c067cd 100644 --- a/packages/block-editor/src/components/block-list/style.native.scss +++ b/packages/block-editor/src/components/block-list/style.native.scss @@ -8,10 +8,6 @@ flex: 1; } -.listDark { - background: #1c1c1e; -} - .switch { flex-direction: row; justify-content: flex-start; diff --git a/packages/block-editor/src/components/inserter/index.native.js b/packages/block-editor/src/components/inserter/index.native.js index 1a757a65a502b2..b64c460f14bd67 100644 --- a/packages/block-editor/src/components/inserter/index.native.js +++ b/packages/block-editor/src/components/inserter/index.native.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { Dropdown, ToolbarButton, Dashicon, withTheme, useStyle } from '@wordpress/components'; +import { Dropdown, ToolbarButton, Dashicon, withTheme } from '@wordpress/components'; import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; @@ -56,9 +56,9 @@ class Inserter extends Component { const { disabled, renderToggle = defaultRenderToggle, - theme, + useStyle, } = this.props; - const style = useStyle( styles.addBlockButton, styles.addBlockButtonDark, theme ); + const style = useStyle( styles.addBlockButton, styles.addBlockButtonDark ); return renderToggle( { onToggle, isOpen, disabled, style } ); } diff --git a/packages/block-editor/src/components/inserter/menu.native.js b/packages/block-editor/src/components/inserter/menu.native.js index 3b0e27a156ddb8..9dbf9b5b162713 100644 --- a/packages/block-editor/src/components/inserter/menu.native.js +++ b/packages/block-editor/src/components/inserter/menu.native.js @@ -14,7 +14,7 @@ import { } from '@wordpress/blocks'; import { withDispatch, withSelect } from '@wordpress/data'; import { withInstanceId, compose } from '@wordpress/compose'; -import { BottomSheet, Icon, withTheme, useStyle } from '@wordpress/components'; +import { BottomSheet, Icon, withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -61,11 +61,12 @@ export class InserterMenu extends Component { } render() { + const { useStyle } = this.props; const numberOfColumns = this.calculateNumberOfColumns(); const bottomPadding = styles.contentBottomPadding; - const modalIconWrapperStyle = useStyle( styles.modalIconWrapper, styles.modalIconWrapperDark, this.props.theme ); - const modalIconStyle = useStyle( styles.modalIcon, styles.modalIconDark, this.props.theme ); - const modalItemLabelStyle = useStyle( styles.modalItemLabel, styles.modalItemLabelDark, this.props.theme ); + const modalIconWrapperStyle = useStyle( styles.modalIconWrapper, styles.modalIconWrapperDark ); + const modalIconStyle = useStyle( styles.modalIcon, styles.modalIconDark ); + const modalItemLabelStyle = useStyle( styles.modalItemLabel, styles.modalItemLabelDark ); return ( <BottomSheet diff --git a/packages/block-editor/src/components/media-placeholder/index.native.js b/packages/block-editor/src/components/media-placeholder/index.native.js index e224cce83c45e4..d2b1bd0d3bf23f 100644 --- a/packages/block-editor/src/components/media-placeholder/index.native.js +++ b/packages/block-editor/src/components/media-placeholder/index.native.js @@ -8,7 +8,7 @@ import { View, Text, TouchableWithoutFeedback } from 'react-native'; */ import { __, sprintf } from '@wordpress/i18n'; import { MediaUpload, MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO } from '@wordpress/block-editor'; -import { withTheme, useStyle } from '@wordpress/components'; +import { withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -16,7 +16,7 @@ import { withTheme, useStyle } from '@wordpress/components'; import styles from './styles.scss'; function MediaPlaceholder( props ) { - const { allowedTypes = [], labels = {}, icon, onSelect, theme } = props; + const { allowedTypes = [], labels = {}, icon, onSelect, useStyle } = props; const isOneType = allowedTypes.length === 1; const isImage = isOneType && allowedTypes.includes( MEDIA_TYPE_IMAGE ); @@ -48,8 +48,8 @@ function MediaPlaceholder( props ) { accessibilityHint = __( 'Double tap to select a video' ); } - const emptyStateContainerStyle = useStyle( styles.emptyStateContainer, styles.emptyStateContainerDark, theme ); - const emptyStateTitleStyle = useStyle( styles.emptyStateTitle, styles.emptyStateTitleDark, theme ); + const emptyStateContainerStyle = useStyle( styles.emptyStateContainer, styles.emptyStateContainerDark ); + const emptyStateTitleStyle = useStyle( styles.emptyStateTitle, styles.emptyStateTitleDark ); return ( <MediaUpload diff --git a/packages/block-editor/src/components/warning/index.native.js b/packages/block-editor/src/components/warning/index.native.js index 071fd30a0738a3..e6c13b5dd75a99 100644 --- a/packages/block-editor/src/components/warning/index.native.js +++ b/packages/block-editor/src/components/warning/index.native.js @@ -6,7 +6,7 @@ import { View, Text } from 'react-native'; /** * WordPress dependencies */ -import { Icon, withTheme, useStyle } from '@wordpress/components'; +import { Icon, withTheme } from '@wordpress/components'; import { normalizeIconObject } from '@wordpress/blocks'; /** @@ -14,15 +14,15 @@ import { normalizeIconObject } from '@wordpress/blocks'; */ import styles from './style.scss'; -function Warning( { title, message, icon, iconClass, theme, ...viewProps } ) { +function Warning( { title, message, icon, iconClass, theme, useStyle, ...viewProps } ) { icon = icon && normalizeIconObject( icon ); const internalIconClass = 'warning-icon' + '-' + theme; - const titleStyle = useStyle( styles.title, styles.titleDark, theme ); - const messageStyle = useStyle( styles.message, styles.messageDark, theme ); + const titleStyle = useStyle( styles.title, styles.titleDark ); + const messageStyle = useStyle( styles.message, styles.messageDark ); return ( <View - style={ useStyle( styles.container, styles.containerDark, theme ) } + style={ useStyle( styles.container, styles.containerDark ) } { ...viewProps } > { icon && ( diff --git a/packages/block-library/src/code/edit.native.js b/packages/block-library/src/code/edit.native.js index 0363fcfebc61d0..a8afae8f0195b4 100644 --- a/packages/block-library/src/code/edit.native.js +++ b/packages/block-library/src/code/edit.native.js @@ -8,7 +8,7 @@ import { View } from 'react-native'; */ import { PlainText } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; -import { withTheme, useStyle } from '@wordpress/components'; +import { withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -23,9 +23,9 @@ import styles from './theme.scss'; // Note: styling is applied directly to the (nested) PlainText component. Web-side components // apply it to the container 'div' but we don't have a proper proposal for cascading styling yet. export function CodeEdit( props ) { - const { attributes, setAttributes, style, onFocus, onBlur, theme } = props; - const codeStyle = useStyle( styles.blockCode, styles.blockCodeDark, theme ); - const placeholderStyle = useStyle( styles.placeholder, styles.placeholderDark, theme ); + const { attributes, setAttributes, style, onFocus, onBlur, useStyle } = props; + const codeStyle = useStyle( styles.blockCode, styles.blockCodeDark ); + const placeholderStyle = useStyle( styles.placeholder, styles.placeholderDark ); return ( <View> diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index 2d059b04c5ca41..eb846520ab53f6 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -20,7 +20,6 @@ import { Toolbar, ToolbarButton, withTheme, - useStyle, } from '@wordpress/components'; import { @@ -195,12 +194,11 @@ class ImageEdit extends React.Component { } getIcon( isRetryIcon ) { - const iconStyle = useStyle( styles.icon, styles.iconDark, this.props.theme ); - if ( isRetryIcon ) { return <Icon icon={ SvgIconRetry } { ...styles.iconRetry } />; } + const iconStyle = this.props.useStyle( styles.icon, styles.iconDark ); return <Icon icon={ SvgIcon } { ...iconStyle } />; } diff --git a/packages/block-library/src/missing/edit.native.js b/packages/block-library/src/missing/edit.native.js index c5a604144b7e7b..356aa68f0fb330 100644 --- a/packages/block-library/src/missing/edit.native.js +++ b/packages/block-library/src/missing/edit.native.js @@ -6,7 +6,7 @@ import { View, Text } from 'react-native'; /** * WordPress dependencies */ -import { Icon, withTheme, useStyle } from '@wordpress/components'; +import { Icon, withTheme } from '@wordpress/components'; import { coreBlocks } from '@wordpress/block-library'; import { normalizeIconObject } from '@wordpress/blocks'; import { Component } from '@wordpress/element'; @@ -20,17 +20,17 @@ import styles from './style.scss'; export class UnsupportedBlockEdit extends Component { render() { const { originalName } = this.props.attributes; - const theme = this.props.theme; + const { useStyle, theme } = this.props; const blockType = coreBlocks[ originalName ]; const title = blockType ? blockType.settings.title : __( 'Unsupported' ); - const titleStyle = useStyle( styles.unsupportedBlockMessage, styles.unsupportedBlockMessageDark, theme ); + const titleStyle = useStyle( styles.unsupportedBlockMessage, styles.unsupportedBlockMessageDark ); const icon = blockType ? normalizeIconObject( blockType.settings.icon ) : 'admin-plugins'; - const iconStyle = useStyle( styles.unsupportedBlockIcon, styles.unsupportedBlockIconDark, theme ); + const iconStyle = useStyle( styles.unsupportedBlockIcon, styles.unsupportedBlockIconDark ); const iconClassName = 'unsupported-icon' + '-' + theme; return ( - <View style={ useStyle( styles.unsupportedBlock, styles.unsupportedBlockDark, theme ) }> + <View style={ useStyle( styles.unsupportedBlock, styles.unsupportedBlockDark ) }> <Icon className={ iconClassName } icon={ icon && icon.src ? icon.src : icon } color={ iconStyle.color } /> <Text style={ titleStyle }>{ title }</Text> </View> diff --git a/packages/block-library/src/more/edit.native.js b/packages/block-library/src/more/edit.native.js index e177213a7f1f6f..c266512b287831 100644 --- a/packages/block-library/src/more/edit.native.js +++ b/packages/block-library/src/more/edit.native.js @@ -9,7 +9,7 @@ import Hr from 'react-native-hr'; */ import { __ } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; -import { withTheme, useStyle } from '@wordpress/components'; +import { withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -26,11 +26,13 @@ export class MoreEdit extends Component { } render() { - const { customText } = this.props.attributes; + const { attributes, useStyle } = this.props; + const { customText } = attributes; const { defaultText } = this.state; + const content = customText || defaultText; - const textStyle = useStyle( styles.moreText, styles.moreTextDark, this.props.theme ); - const lineStyle = useStyle( styles.moreLine, styles.moreLineDark, this.props.theme ); + const textStyle = useStyle( styles.moreText, styles.moreTextDark ); + const lineStyle = useStyle( styles.moreLine, styles.moreLineDark ); return ( <View> diff --git a/packages/block-library/src/nextpage/edit.native.js b/packages/block-library/src/nextpage/edit.native.js index be9ad283576401..37f9c235b7ef6f 100644 --- a/packages/block-library/src/nextpage/edit.native.js +++ b/packages/block-library/src/nextpage/edit.native.js @@ -8,19 +8,19 @@ import Hr from 'react-native-hr'; * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { withTheme, useStyle } from '@wordpress/components'; +import { withTheme } from '@wordpress/components'; /** * Internal dependencies */ import styles from './editor.scss'; -export function NextPageEdit( { attributes, isSelected, onFocus, theme } ) { +export function NextPageEdit( { attributes, isSelected, onFocus, useStyle } ) { const { customText = __( 'Page break' ) } = attributes; const accessibilityTitle = attributes.customText || ''; const accessibilityState = isSelected ? [ 'selected' ] : []; - const textStyle = useStyle( styles.nextpageText, styles.nextpageTextDark, theme ); - const lineStyle = useStyle( styles.nextpageLine, styles.nextpageLineDark, theme ); + const textStyle = useStyle( styles.nextpageText, styles.nextpageTextDark ); + const lineStyle = useStyle( styles.nextpageLine, styles.nextpageLineDark ); return ( <View diff --git a/packages/block-library/src/video/edit.native.js b/packages/block-library/src/video/edit.native.js index 4b803ab9d97a95..39fc0e0d2233df 100644 --- a/packages/block-library/src/video/edit.native.js +++ b/packages/block-library/src/video/edit.native.js @@ -21,7 +21,6 @@ import { Toolbar, ToolbarButton, withTheme, - useStyle, } from '@wordpress/components'; import { @@ -150,12 +149,11 @@ class VideoEdit extends React.Component { } getIcon( isRetryIcon, isMediaPlaceholder ) { - const iconStyle = useStyle( style.icon, style.iconDark, this.props.theme ); - if ( isRetryIcon ) { return <Icon icon={ SvgIconRetry } { ...style.icon } />; } + const iconStyle = this.props.useStyle( style.icon, style.iconDark ); return <Icon icon={ SvgIcon } { ...( ! isMediaPlaceholder ? style.iconUploading : iconStyle ) } />; } diff --git a/packages/components/src/mobile/bottom-sheet/cell.native.js b/packages/components/src/mobile/bottom-sheet/cell.native.js index cbc10928478704..6a5fc6ba38947f 100644 --- a/packages/components/src/mobile/bottom-sheet/cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/cell.native.js @@ -16,8 +16,7 @@ import { __, _x, sprintf } from '@wordpress/i18n'; */ import styles from './styles.scss'; import platformStyles from './cellStyles.scss'; -// `useStyle as getStyle`: Hack to avoid lint thinking this is a React Hook -import { withTheme, useStyle as getStyle } from '../dark-mode'; +import { withTheme } from '../dark-mode'; class BottomSheetCell extends Component { constructor( props ) { @@ -50,14 +49,14 @@ class BottomSheetCell extends Component { editable = true, separatorType, style = {}, - theme, + useStyle, ...valueProps } = this.props; const showValue = value !== undefined; const isValueEditable = editable && onChangeValue !== undefined; - const cellLabelStyle = getStyle( styles.cellLabel, styles.cellTextDark, theme ); - const cellLabelCenteredStyle = getStyle( styles.cellLabelCentered, styles.cellTextDark, theme ); + const cellLabelStyle = useStyle( styles.cellLabel, styles.cellTextDark ); + const cellLabelCenteredStyle = useStyle( styles.cellLabelCentered, styles.cellTextDark ); const defaultLabelStyle = showValue || icon !== undefined ? cellLabelStyle : cellLabelCenteredStyle; const drawSeparator = ( separatorType && separatorType !== 'none' ) || separatorStyle === undefined; @@ -81,8 +80,8 @@ class BottomSheetCell extends Component { const separatorStyle = () => { //eslint-disable-next-line @wordpress/no-unused-vars-before-return - const defaultSeparatorStyle = getStyle( styles.separator, styles.separatorDark, theme ); - const cellSeparatorStyle = getStyle( styles.cellSeparator, styles.cellSeparatorDark, theme ); + const defaultSeparatorStyle = this.props.useStyle( styles.separator, styles.separatorDark ); + const cellSeparatorStyle = this.props.useStyle( styles.cellSeparator, styles.cellSeparatorDark ); const leftMarginStyle = { ...cellSeparatorStyle, ...platformStyles.separatorMarginLeft }; switch ( separatorType ) { case 'leftMargin': @@ -98,7 +97,7 @@ class BottomSheetCell extends Component { const getValueComponent = () => { const styleRTL = I18nManager.isRTL && styles.cellValueRTL; - const cellValueStyle = getStyle( styles.cellValue, styles.cellTextDark, theme ); + const cellValueStyle = this.props.useStyle( styles.cellValue, styles.cellTextDark ); const finalStyle = { ...cellValueStyle, ...valueStyle, ...styleRTL }; // To be able to show the `middle` ellipsizeMode on editable cells @@ -151,7 +150,7 @@ class BottomSheetCell extends Component { ); }; - const iconStyle = getStyle( styles.icon, styles.iconDark, theme ); + const iconStyle = useStyle( styles.icon, styles.iconDark ); return ( <TouchableOpacity diff --git a/packages/components/src/mobile/bottom-sheet/index.native.js b/packages/components/src/mobile/bottom-sheet/index.native.js index e3209713e49449..805ddb084c0587 100644 --- a/packages/components/src/mobile/bottom-sheet/index.native.js +++ b/packages/components/src/mobile/bottom-sheet/index.native.js @@ -19,7 +19,7 @@ import Cell from './cell'; import PickerCell from './picker-cell'; import SwitchCell from './switch-cell'; import KeyboardAvoidingView from './keyboard-avoiding-view'; -import { withTheme, useStyle } from '../dark-mode'; +import { withTheme } from '../dark-mode'; class BottomSheet extends Component { constructor() { @@ -64,7 +64,7 @@ class BottomSheet extends Component { hideHeader, style = {}, contentStyle = {}, - theme, + useStyle, } = this.props; const panResponder = PanResponder.create( { @@ -120,7 +120,7 @@ class BottomSheet extends Component { }, }; - const backgroundStyle = useStyle( styles.background, styles.backgroundDark, theme ); + const backgroundStyle = useStyle( styles.background, styles.backgroundDark ); return ( <Modal diff --git a/packages/components/src/mobile/dark-mode/index.native.js b/packages/components/src/mobile/dark-mode/index.native.js index d2b13020f027f9..44ff4c38a8f644 100644 --- a/packages/components/src/mobile/dark-mode/index.native.js +++ b/packages/components/src/mobile/dark-mode/index.native.js @@ -4,27 +4,18 @@ import { eventEmitter, initialMode } from 'react-native-dark-mode'; import React from 'react'; -// This was failing on CI +// Conditional needed to pass UI Tests on CI if ( eventEmitter.setMaxListeners ) { eventEmitter.setMaxListeners( 150 ); } -export function useStyle( light, dark, theme ) { - const finalDark = { - ...light, - ...dark, - }; - - return theme === 'dark' ? finalDark : light; -} - -// This function takes a component... export function withTheme( WrappedComponent ) { return class extends React.Component { constructor( props ) { super( props ); this.onModeChanged = this.onModeChanged.bind( this ); + this.useStyle = this.useStyle.bind( this ); this.state = { mode: initialMode, @@ -46,8 +37,22 @@ export function withTheme( WrappedComponent ) { } } + useStyle( light, dark ) { + const isDarkMode = this.state.mode === 'dark'; + const finalDark = { + ...light, + ...dark, + }; + + return isDarkMode ? finalDark : light; + } + render() { - return <WrappedComponent theme={ this.state.mode } { ...this.props } />; + return <WrappedComponent + theme={ this.state.mode } + useStyle={ this.useStyle } + { ...this.props } + />; } }; } diff --git a/packages/components/src/mobile/html-text-input/index.native.js b/packages/components/src/mobile/html-text-input/index.native.js index 1408be65c90530..4032fa5a74b0d5 100644 --- a/packages/components/src/mobile/html-text-input/index.native.js +++ b/packages/components/src/mobile/html-text-input/index.native.js @@ -15,7 +15,7 @@ import { withInstanceId, compose } from '@wordpress/compose'; /** * Internal dependencies */ -import { withTheme, useStyle } from '../dark-mode'; +import { withTheme } from '../dark-mode'; import HTMLInputContainer from './container'; import styles from './style.scss'; @@ -61,8 +61,9 @@ export class HTMLTextInput extends Component { } render() { - const htmlStyle = useStyle( styles.htmlView, styles.htmlViewDark, this.props.theme ); - const placeholderStyle = useStyle( styles.placeholder, styles.placeholderDark, this.props.theme ); + const { useStyle } = this.props; + const htmlStyle = useStyle( styles.htmlView, styles.htmlViewDark ); + const placeholderStyle = useStyle( styles.placeholder, styles.placeholderDark ); return ( <HTMLInputContainer parentHeight={ this.props.parentHeight }> <TextInput diff --git a/packages/components/src/mobile/html-text-input/test/index.native.js b/packages/components/src/mobile/html-text-input/test/index.native.js index 479846d3f6a960..4a10c0159293df 100644 --- a/packages/components/src/mobile/html-text-input/test/index.native.js +++ b/packages/components/src/mobile/html-text-input/test/index.native.js @@ -33,10 +33,14 @@ const findTitleTextInput = ( wrapper ) => { return findTextInputInWrapper( wrapper, { placeholder } ); }; +const useStyle = () => { + return { color: 'white' }; +}; + describe( 'HTMLTextInput', () => { it( 'HTMLTextInput renders', () => { const wrapper = shallow( - <HTMLTextInput /> + <HTMLTextInput useStyle={ useStyle } /> ); expect( wrapper ).toBeTruthy(); } ); @@ -47,6 +51,7 @@ describe( 'HTMLTextInput', () => { const wrapper = shallow( <HTMLTextInput onChange={ onChange } + useStyle={ useStyle } /> ); @@ -71,6 +76,7 @@ describe( 'HTMLTextInput', () => { <HTMLTextInput onPersist={ onPersist } onChange={ jest.fn() } + useStyle={ useStyle } /> ); @@ -101,6 +107,7 @@ describe( 'HTMLTextInput', () => { const wrapper = shallow( <HTMLTextInput editTitle={ editTitle } + useStyle={ useStyle } /> ); diff --git a/packages/components/src/toolbar/toolbar-container.native.js b/packages/components/src/toolbar/toolbar-container.native.js index f9d019450266c2..c92251c38c19cf 100644 --- a/packages/components/src/toolbar/toolbar-container.native.js +++ b/packages/components/src/toolbar/toolbar-container.native.js @@ -7,11 +7,11 @@ import { View } from 'react-native'; * Internal dependencies */ import styles from './style.scss'; -import { withTheme, useStyle } from '../mobile/dark-mode'; +import { withTheme } from '../mobile/dark-mode'; -const ToolbarContainer = ( props ) => ( - <View style={ [ useStyle( styles.container, styles.containerDark, props.theme ), props.passedStyle ] }> - { props.children } +const ToolbarContainer = ( { useStyle, passedStyle, children } ) => ( + <View style={ [ useStyle( styles.container, styles.containerDark ), passedStyle ] }> + { children } </View> ); diff --git a/packages/edit-post/src/components/header/header-toolbar/index.native.js b/packages/edit-post/src/components/header/header-toolbar/index.native.js index 278b0af30548e1..5da763a5dd8e5d 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.native.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.native.js @@ -15,7 +15,7 @@ import { Inserter, BlockToolbar, } from '@wordpress/block-editor'; -import { Toolbar, ToolbarButton, useStyle, withTheme } from '@wordpress/components'; +import { Toolbar, ToolbarButton, withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -30,7 +30,7 @@ function HeaderToolbar( { undo, showInserter, showKeyboardHideButton, - theme, + useStyle, onHideKeyboard, } ) { const scrollViewRef = useRef( null ); @@ -39,7 +39,7 @@ function HeaderToolbar( { }; return ( - <View style={ useStyle( styles.container, styles.containerDark, theme ) }> + <View style={ useStyle( styles.container, styles.containerDark ) }> <ScrollView ref={ scrollViewRef } onContentSizeChange={ scrollToStart } diff --git a/packages/edit-post/src/components/layout/index.native.js b/packages/edit-post/src/components/layout/index.native.js index 5a10ed14cb9a56..36250cf27eb96f 100644 --- a/packages/edit-post/src/components/layout/index.native.js +++ b/packages/edit-post/src/components/layout/index.native.js @@ -11,7 +11,7 @@ import { sendNativeEditorDidLayout } from 'react-native-gutenberg-bridge'; import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; -import { HTMLTextInput, KeyboardAvoidingView, ReadableContentView, useStyle, withTheme } from '@wordpress/components'; +import { HTMLTextInput, KeyboardAvoidingView, ReadableContentView, withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -99,6 +99,7 @@ class Layout extends Component { render() { const { mode, + useStyle, } = this.props; const isHtmlView = mode === 'text'; @@ -114,8 +115,8 @@ class Layout extends Component { }; return ( - <SafeAreaView style={ useStyle( styles.container, styles.containerDark, this.props.theme ) } onLayout={ this.onRootViewLayout }> - <View style={ useStyle( styles.background, styles.backgroundDark, this.props.theme ) }> + <SafeAreaView style={ useStyle( styles.container, styles.containerDark ) } onLayout={ this.onRootViewLayout }> + <View style={ useStyle( styles.background, styles.backgroundDark ) }> { isHtmlView ? this.renderHTML() : this.renderVisual() } </View> <View style={ { flex: 0, flexBasis: marginBottom, height: marginBottom } } /> diff --git a/packages/edit-post/src/components/visual-editor/index.native.js b/packages/edit-post/src/components/visual-editor/index.native.js index b7c53b12aad545..1d8c7dbc094aff 100644 --- a/packages/edit-post/src/components/visual-editor/index.native.js +++ b/packages/edit-post/src/components/visual-editor/index.native.js @@ -7,7 +7,7 @@ import { withDispatch, withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import { BlockList } from '@wordpress/block-editor'; import { PostTitle } from '@wordpress/editor'; -import { ReadableContentView, withTheme, useStyle } from '@wordpress/components'; +import { ReadableContentView, withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -20,9 +20,9 @@ class VisualEditor extends Component { editTitle, setTitleRef, title, - theme, + useStyle, } = this.props; - const blockHolderFocusedStyle = useStyle( styles.blockHolderFocused, styles.blockHolderFocusedDark, theme ); + const blockHolderFocusedStyle = useStyle( styles.blockHolderFocused, styles.blockHolderFocusedDark ); return ( <ReadableContentView> <PostTitle diff --git a/packages/rich-text/src/component/index.native.js b/packages/rich-text/src/component/index.native.js index 6191e627d56962..9c3e44440dfd5b 100644 --- a/packages/rich-text/src/component/index.native.js +++ b/packages/rich-text/src/component/index.native.js @@ -20,7 +20,7 @@ import { childrenBlock } from '@wordpress/blocks'; import { decodeEntities } from '@wordpress/html-entities'; import { BACKSPACE } from '@wordpress/keycodes'; import { isURL } from '@wordpress/url'; -import { useStyle, withTheme } from '@wordpress/components'; +import { withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -771,7 +771,7 @@ export class RichText extends Component { style, __unstableIsSelected: isSelected, children, - theme, + useStyle, } = this.props; const record = this.getRecord(); @@ -782,7 +782,7 @@ export class RichText extends Component { minHeight = style.minHeight; } - const placeholderStyle = useStyle( styles.richTextPlaceholder, styles.richTextPlaceholderDark, theme ); + const placeholderStyle = useStyle( styles.richTextPlaceholder, styles.richTextPlaceholderDark ); const { color: defaultPlaceholderTextColor, @@ -792,7 +792,7 @@ export class RichText extends Component { color: defaultColor, textDecorationColor: defaultTextDecorationColor, fontFamily: defaultFontFamily, - } = useStyle( styles.richText, styles.richTextDark, theme ); + } = useStyle( styles.richText, styles.richTextDark ); let selection = null; if ( this.needsSelectionUpdate ) { @@ -821,7 +821,7 @@ export class RichText extends Component { this.firedAfterTextChanged = false; } - const dynamicStyle = useStyle( style, styles.richTextDark, theme ); + const dynamicStyle = useStyle( style, styles.richTextDark ); return ( <View> diff --git a/packages/rich-text/src/component/test/index.native.js b/packages/rich-text/src/component/test/index.native.js index ec0cbb77195244..c4ff0da2b2f415 100644 --- a/packages/rich-text/src/component/test/index.native.js +++ b/packages/rich-text/src/component/test/index.native.js @@ -8,6 +8,10 @@ import { shallow } from 'enzyme'; */ import { RichText } from '../index'; +const useStyle = () => { + return { color: 'white' }; +}; + describe( 'RichText Native', () => { let richText; @@ -40,6 +44,7 @@ describe( 'RichText Native', () => { } } formatTypes={ [] } onSelectionChange={ jest.fn() } + useStyle={ useStyle } /> ); const event = { From 648a1b9280a343c4b921781577a9d048db1d7027 Mon Sep 17 00:00:00 2001 From: Danilo Ercoli <ercoli@gmail.com> Date: Thu, 19 Sep 2019 09:47:44 -0400 Subject: [PATCH 20/31] [RNMobile] Add autosave to mobile apps (#17329) * [RNMobile] Fix crash when adding separator * Build: remove global install of latest npm since we want to use the paired node/npm version (#17134) * Build: remove global install of latest npm since we want to use the paired node/npm version * Also update travis to remove --latest-npm flag * [RNMobile] Try dark mode (iOS) (#17067) * Adding dark mode component implemented on list and list block * Adding DarkMode handling to RichText, ToolBar and SafeArea * Mobile: Using DarkMode as HOC * iOS DarkMode: Modified colors on block list and block container * iOS DarkMode: Improved Header Toolbar colors * iOS DarkMode: Removing background from buttons * iOS DarkMode warning and unsupported * iOS DarkMode: MediaPlaceholder * iOS DarkMode: BottomSheets * iOS DarkMode: Inserter * iOS DarkMode: DefaultBlockAppender * iOS DarkMode: PostTite * Update hardcoded colors with variables * iOS DarkMode: Fix bottom-sheet cell value color * iOS DarkMode: More - PageBreak - Add Block Here * iOS DarkMode: Better text color * iOS Darkmode: Code block * iOS DarkMode: HTML View * iOS DarkMode: Improve colors on SafeArea * Fix toolbar not avoiding keyboard regression * Fix native unit tests * Fix gutenberg-mobile unit tests * Adding RNDarkMode mocks * RNMobile: Fix crash when viewing HTML on iOS * [RNMobile] Remove toolbar from html view * [RNMobile] Fix MaxListenersExceededWarning caused by dark-mode event emitter (#17186) * Fix MaxListenersExceededWarning caused by dark-mode event emitter * Checking for setMaxListeners trying to avoid CI error * Adding remove listener to DarkMode HOC * DarkMode: Binding this.onModeChanged to `this` * DarkMode: Adding conditional needed to pass UI Tests on CI * Fix focus title on new posts regression (#17180) * BottomSheet: Setting DashIcon color directly when theme is default (light) (#17193) * Add a preliminary version of the AutosaveMonitor for mobile that calls the "bridge" and asks the native side to save the content * Add autosave mock function for tests * Fix merge conflicts * Fix lint * Re-add autosave on mobile that was removed erroneously during import-merge from rnmobile/master * Remove native variant of AutosaveMonitor and introduces changes at editor store level * Default to false for `isEditedPostAutosaveable` on mobile. There was a typo in the returing value on the previous commit. * Make sure to consider edits to the Title when checking if auto-save is needed * Fix lint --- .../src/components/layout/index.native.js | 2 + .../editor/src/components/index.native.js | 1 + packages/editor/src/store/actions.native.js | 13 ++++++ packages/editor/src/store/reducer.native.js | 4 ++ packages/editor/src/store/selectors.native.js | 42 +++++++++++++++++++ test/native/setup.js | 1 + 6 files changed, 63 insertions(+) diff --git a/packages/edit-post/src/components/layout/index.native.js b/packages/edit-post/src/components/layout/index.native.js index 36250cf27eb96f..44bef0e3bec51b 100644 --- a/packages/edit-post/src/components/layout/index.native.js +++ b/packages/edit-post/src/components/layout/index.native.js @@ -12,6 +12,7 @@ import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; import { compose } from '@wordpress/compose'; import { HTMLTextInput, KeyboardAvoidingView, ReadableContentView, withTheme } from '@wordpress/components'; +import { AutosaveMonitor } from '@wordpress/editor'; /** * Internal dependencies @@ -116,6 +117,7 @@ class Layout extends Component { return ( <SafeAreaView style={ useStyle( styles.container, styles.containerDark ) } onLayout={ this.onRootViewLayout }> + <AutosaveMonitor /> <View style={ useStyle( styles.background, styles.backgroundDark ) }> { isHtmlView ? this.renderHTML() : this.renderVisual() } </View> diff --git a/packages/editor/src/components/index.native.js b/packages/editor/src/components/index.native.js index 69035455d49f13..45156604efe6f4 100644 --- a/packages/editor/src/components/index.native.js +++ b/packages/editor/src/components/index.native.js @@ -1,5 +1,6 @@ // Post Related Components +export { default as AutosaveMonitor } from './autosave-monitor'; export { default as PostTitle } from './post-title'; export { default as EditorHistoryRedo } from './editor-history/redo'; export { default as EditorHistoryUndo } from './editor-history/undo'; diff --git a/packages/editor/src/store/actions.native.js b/packages/editor/src/store/actions.native.js index 51741ac9bc3b44..17733a0e47b408 100644 --- a/packages/editor/src/store/actions.native.js +++ b/packages/editor/src/store/actions.native.js @@ -1,3 +1,7 @@ +/** + * External dependencies + */ +import RNReactNativeGutenbergBridge from 'react-native-gutenberg-bridge'; export * from './actions.js'; @@ -14,3 +18,12 @@ export function togglePostTitleSelection( isSelected = true ) { isSelected, }; } + +/** + * Action generator used in signalling that the post should autosave. + * + * @param {Object?} options Extra flags to identify the autosave. + */ +export function* autosave( ) { + RNReactNativeGutenbergBridge.editorDidAutosave(); +} diff --git a/packages/editor/src/store/reducer.native.js b/packages/editor/src/store/reducer.native.js index 82b3689a98ead0..0c2c4e7d24e704 100644 --- a/packages/editor/src/store/reducer.native.js +++ b/packages/editor/src/store/reducer.native.js @@ -26,6 +26,10 @@ import { editorSettings, } from './reducer.js'; +import { EDITOR_SETTINGS_DEFAULTS } from './defaults.js'; + +EDITOR_SETTINGS_DEFAULTS.autosaveInterval = 2; // This is a way to override default on mobile + export * from './reducer.js'; /** diff --git a/packages/editor/src/store/selectors.native.js b/packages/editor/src/store/selectors.native.js index 8c6ead8e97ba2f..ae84e7b5afe5a1 100644 --- a/packages/editor/src/store/selectors.native.js +++ b/packages/editor/src/store/selectors.native.js @@ -1,3 +1,16 @@ +/** + * WordPress dependencies + */ +import { createRegistrySelector } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { + isEditedPostDirty, + isEditedPostSaveable, + hasChangedContent, +} from './selectors.js'; export * from './selectors.js'; @@ -11,3 +24,32 @@ export * from './selectors.js'; export function isPostTitleSelected( state ) { return state.postTitle.isSelected; } + +/** + * Returns true if the post can be autosaved, or false otherwise. + * + * @param {Object} state Global application state. + * @param {Object} autosave A raw autosave object from the REST API. + * + * @return {boolean} Whether the post can be autosaved. + */ +export const isEditedPostAutosaveable = createRegistrySelector( ( ) => function( state ) { + // A post must contain a title, an excerpt, or non-empty content to be valid for autosaving. + if ( ! isEditedPostSaveable( state ) ) { + return false; + } + + // To avoid an expensive content serialization, use the content dirtiness + // flag in place of content field comparison against the known autosave. + // This is not strictly accurate, and relies on a tolerance toward autosave + // request failures for unnecessary saves. + if ( hasChangedContent( state ) ) { + return true; + } + + if ( isEditedPostDirty( state ) ) { + return true; + } + + return false; +} ); diff --git a/test/native/setup.js b/test/native/setup.js index c0e3e0942a9180..f34947ed9958c0 100644 --- a/test/native/setup.js +++ b/test/native/setup.js @@ -14,6 +14,7 @@ jest.mock( 'react-native-gutenberg-bridge', () => { subscribeUpdateHtml: jest.fn(), subscribeMediaAppend: jest.fn(), editorDidMount: jest.fn(), + editorDidAutosave: jest.fn(), subscribeMediaUpload: jest.fn(), requestMediaPickFromMediaLibrary: jest.fn(), requestMediaPickFromDeviceLibrary: jest.fn(), From e99d365a737a6b9d0dfabe86502e0e8c3ccc4d4b Mon Sep 17 00:00:00 2001 From: Luke Walczak <lukasz.walczak.pwr@gmail.com> Date: Wed, 25 Sep 2019 10:29:20 +0200 Subject: [PATCH 21/31] Add isAppender functionality on mobile (#17195) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add isAppender functionality on mobile * refactor isAppender conditions * Replace dropZoneUIOnly in favour of showMediaSelectionUI * deprecate dropZoneUIOnly and add disableMediaSelection prop * Update test * Refactor tests and change prop name * Remove redundant empty lines * Refactor conditions inside MediaPlaceholder * Update block-editor CHANGELOG * Update packages/block-editor/CHANGELOG.md Co-Authored-By: Grzegorz (Greg) Ziółkowski <grzegorz@gziolo.pl> --- packages/block-editor/CHANGELOG.md | 6 + .../components/media-placeholder/README.md | 11 +- .../src/components/media-placeholder/index.js | 10 +- .../media-placeholder/index.native.js | 111 ++++++++++++------ .../media-placeholder/styles.native.scss | 15 +++ packages/block-library/src/gallery/edit.js | 2 +- packages/block-library/src/image/edit.js | 2 +- 7 files changed, 118 insertions(+), 39 deletions(-) diff --git a/packages/block-editor/CHANGELOG.md b/packages/block-editor/CHANGELOG.md index 07abd2b81221bb..8b2a631518de53 100644 --- a/packages/block-editor/CHANGELOG.md +++ b/packages/block-editor/CHANGELOG.md @@ -1,3 +1,9 @@ +## Master + +### Deprecation + +- `dropZoneUIOnly` prop in `MediaPlaceholder` component has been deprecated in favor of `disableMediaButtons` prop. + ## 3.0.0 (2019-08-05) ### New Features diff --git a/packages/block-editor/src/components/media-placeholder/README.md b/packages/block-editor/src/components/media-placeholder/README.md index bde6c7598042ba..11f451a3ad629f 100644 --- a/packages/block-editor/src/components/media-placeholder/README.md +++ b/packages/block-editor/src/components/media-placeholder/README.md @@ -85,7 +85,16 @@ If false the default placeholder style is used. - Type: `Boolean` - Required: No - Default: `false` -- Platform: Web +- Platform: Web | Mobile + +### disableMediaButtons + +If true, only the Drop Zone will be rendered. No UI controls to upload the media will be shown + +- Type: `Boolean` +- Required: No +- Default: `false` +- Platform: Web | Mobile ### labels diff --git a/packages/block-editor/src/components/media-placeholder/index.js b/packages/block-editor/src/components/media-placeholder/index.js index efad860f586e7a..2f496600269036 100644 --- a/packages/block-editor/src/components/media-placeholder/index.js +++ b/packages/block-editor/src/components/media-placeholder/index.js @@ -25,6 +25,7 @@ import { __ } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; import { compose } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; +import deprecated from '@wordpress/deprecated'; /** * Internal dependencies @@ -387,10 +388,17 @@ export class MediaPlaceholder extends Component { render() { const { + disableMediaButtons, dropZoneUIOnly, } = this.props; - if ( dropZoneUIOnly ) { + if ( dropZoneUIOnly || disableMediaButtons ) { + if ( dropZoneUIOnly ) { + deprecated( 'wp.blockEditor.MediaPlaceholder dropZoneUIOnly prop', { + alternative: 'disableMediaButtons', + } ); + } + return ( <MediaUploadCheck> { this.renderDropZone() } diff --git a/packages/block-editor/src/components/media-placeholder/index.native.js b/packages/block-editor/src/components/media-placeholder/index.native.js index d2b1bd0d3bf23f..a9a7d50fce11fd 100644 --- a/packages/block-editor/src/components/media-placeholder/index.native.js +++ b/packages/block-editor/src/components/media-placeholder/index.native.js @@ -7,8 +7,12 @@ import { View, Text, TouchableWithoutFeedback } from 'react-native'; * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { MediaUpload, MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO } from '@wordpress/block-editor'; -import { withTheme } from '@wordpress/components'; +import { + MediaUpload, + MEDIA_TYPE_IMAGE, + MEDIA_TYPE_VIDEO, +} from '@wordpress/block-editor'; +import { Dashicon, withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -16,7 +20,15 @@ import { withTheme } from '@wordpress/components'; import styles from './styles.scss'; function MediaPlaceholder( props ) { - const { allowedTypes = [], labels = {}, icon, onSelect, useStyle } = props; + const { + allowedTypes = [], + labels = {}, + icon, + onSelect, + isAppender, + disableMediaButtons, + useStyle, + } = props; const isOneType = allowedTypes.length === 1; const isImage = isOneType && allowedTypes.includes( MEDIA_TYPE_IMAGE ); @@ -51,40 +63,69 @@ function MediaPlaceholder( props ) { const emptyStateContainerStyle = useStyle( styles.emptyStateContainer, styles.emptyStateContainerDark ); const emptyStateTitleStyle = useStyle( styles.emptyStateTitle, styles.emptyStateTitleDark ); + const renderContent = () => { + if ( isAppender === undefined || ! isAppender ) { + return ( + <> + <View style={ styles.modalIcon }> + { icon } + </View> + <Text style={ emptyStateTitleStyle }> + { placeholderTitle } + </Text> + <Text style={ styles.emptyStateDescription }> + { instructions } + </Text> + </> + ); + } else if ( isAppender && ! disableMediaButtons ) { + return ( + <Dashicon + icon="plus-alt" + style={ styles.addBlockButton } + color={ styles.addBlockButton.color } + size={ styles.addBlockButton.size } + /> + ); + } + }; + + if ( isAppender && disableMediaButtons ) { + return null; + } + return ( - <MediaUpload - allowedTypes={ allowedTypes } - onSelect={ onSelect } - render={ ( { open, getMediaOptions } ) => { - return ( - <TouchableWithoutFeedback - accessibilityLabel={ sprintf( - /* translators: accessibility text for the media block empty state. %s: media type */ - __( '%s block. Empty' ), - placeholderTitle - ) } - accessibilityRole={ 'button' } - accessibilityHint={ accessibilityHint } - onPress={ ( event ) => { - props.onFocus( event ); - open(); - } } - > - <View style={ emptyStateContainerStyle }> - { getMediaOptions() } - <View style={ styles.modalIcon }> - { icon } + <View style={ { flex: 1 } }> + <MediaUpload + allowedTypes={ allowedTypes } + onSelect={ onSelect } + render={ ( { open, getMediaOptions } ) => { + return ( + <TouchableWithoutFeedback + accessibilityLabel={ sprintf( + /* translators: accessibility text for the media block empty state. %s: media type */ + __( '%s block. Empty' ), + placeholderTitle + ) } + accessibilityRole={ 'button' } + accessibilityHint={ accessibilityHint } + onPress={ ( event ) => { + props.onFocus( event ); + open(); + } }> + <View + style={ [ + emptyStateContainerStyle, + isAppender && styles.isAppender, + ] }> + { getMediaOptions() } + { renderContent() } </View> - <Text style={ emptyStateTitleStyle }> - { placeholderTitle } - </Text> - <Text style={ styles.emptyStateDescription }> - { instructions } - </Text> - </View> - </TouchableWithoutFeedback> - ); - } } /> + </TouchableWithoutFeedback> + ); + } } + /> + </View> ); } diff --git a/packages/block-editor/src/components/media-placeholder/styles.native.scss b/packages/block-editor/src/components/media-placeholder/styles.native.scss index 7a192f078e2090..83f201579a2ab6 100644 --- a/packages/block-editor/src/components/media-placeholder/styles.native.scss +++ b/packages/block-editor/src/components/media-placeholder/styles.native.scss @@ -41,3 +41,18 @@ align-items: center; fill: $gray-dark; } + +.isAppender { + height: auto; + background-color: $white; + border: $border-width solid $light-gray-500; + border-radius: 4px; +} + +.addBlockButton { + color: $white; + background-color: $dark-gray-500; + border-radius: $icon-button-size-small / 2; + overflow: hidden; + size: $icon-button-size-small; +} diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js index 129fc2343224a6..13040d1f950081 100644 --- a/packages/block-library/src/gallery/edit.js +++ b/packages/block-library/src/gallery/edit.js @@ -244,7 +244,7 @@ class GalleryEdit extends Component { addToGallery={ hasImages } isAppender={ hasImages } className={ className } - dropZoneUIOnly={ hasImages && ! isSelected } + disableMediaButtons={ hasImages && ! isSelected } icon={ ! hasImages && <BlockIcon icon={ icon } /> } labels={ { title: ! hasImages && __( 'Gallery' ), diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index e0cc5e751fdaaf..8f34fa1be7b29b 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -673,7 +673,7 @@ export class ImageEdit extends Component { allowedTypes={ ALLOWED_MEDIA_TYPES } value={ { id, src } } mediaPreview={ mediaPreview } - dropZoneUIOnly={ ! isEditing && url } + disableMediaButtons={ ! isEditing && url } /> ); if ( isEditing || ! url ) { From 69da85e5b89baffd2871bda84358bb7e2fffbe8b Mon Sep 17 00:00:00 2001 From: Danilo Ercoli <ercoli@gmail.com> Date: Wed, 25 Sep 2019 11:08:33 +0200 Subject: [PATCH 22/31] Autosave monitor - Make the mobile editor ping the native at each keystroke, since the deboucing logic is already well defined in the apps. (#17548) --- packages/editor/src/store/reducer.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/editor/src/store/reducer.native.js b/packages/editor/src/store/reducer.native.js index 0c2c4e7d24e704..7657101e6ea7a4 100644 --- a/packages/editor/src/store/reducer.native.js +++ b/packages/editor/src/store/reducer.native.js @@ -28,7 +28,7 @@ import { import { EDITOR_SETTINGS_DEFAULTS } from './defaults.js'; -EDITOR_SETTINGS_DEFAULTS.autosaveInterval = 2; // This is a way to override default on mobile +EDITOR_SETTINGS_DEFAULTS.autosaveInterval = 0; // This is a way to override default behavior on mobile, and make it ping the native save at each keystroke export * from './reducer.js'; From ae6d2ceba43e64b8337df2401d7a70458f930d8b Mon Sep 17 00:00:00 2001 From: Tugdual de Kerviler <dekervit@gmail.com> Date: Wed, 25 Sep 2019 13:19:10 +0200 Subject: [PATCH 23/31] [RNMobile] Refactor Dark Mode HOC (#17552) * [RNMobile] Refactor the Dark Mode HOC to fix naming antipatterns * Fix lint errors * Add .native.js suffix to usePreferredColorScheme * Update usage of theme props renamed to preferredColorScheme * Update usage of theme props renamed to preferredColorScheme --- .../src/components/block-list/index.native.js | 12 ++-- .../src/components/inserter/index.native.js | 10 ++-- .../src/components/inserter/menu.native.js | 14 ++--- .../media-placeholder/index.native.js | 11 ++-- .../src/components/warning/index.native.js | 15 ++--- .../block-library/src/code/edit.native.js | 10 ++-- .../block-library/src/image/edit.native.js | 6 +- .../block-library/src/missing/edit.native.js | 15 ++--- .../block-library/src/more/edit.native.js | 10 ++-- .../block-library/src/nextpage/edit.native.js | 10 ++-- .../block-library/src/video/edit.native.js | 7 +-- packages/components/src/index.native.js | 1 - .../src/mobile/bottom-sheet/cell.native.js | 18 +++--- .../src/mobile/bottom-sheet/index.native.js | 8 +-- .../src/mobile/dark-mode/index.native.js | 58 ------------------- .../mobile/html-text-input/index.native.js | 11 ++-- .../html-text-input/test/index.native.js | 10 ++-- .../src/toolbar/toolbar-container.native.js | 12 ++-- .../index.native.js | 32 ++++++++++ .../index.native.js | 13 +++++ packages/compose/src/index.native.js | 9 +++ .../header/header-toolbar/index.native.js | 10 ++-- .../src/components/layout/index.native.js | 12 ++-- .../components/visual-editor/index.native.js | 10 ++-- .../src/link/test/modal.native.js | 3 +- .../rich-text/src/component/index.native.js | 13 ++--- .../src/component/test/index.native.js | 4 +- test/native/setup.js | 5 +- 28 files changed, 172 insertions(+), 177 deletions(-) delete mode 100644 packages/components/src/mobile/dark-mode/index.native.js create mode 100644 packages/compose/src/higher-order/with-preferred-color-scheme/index.native.js create mode 100644 packages/compose/src/hooks/use-preferred-color-scheme/index.native.js create mode 100644 packages/compose/src/index.native.js 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 54b6b62f08c96b..0eefa1274378c8 100644 --- a/packages/block-editor/src/components/block-list/index.native.js +++ b/packages/block-editor/src/components/block-list/index.native.js @@ -10,9 +10,9 @@ import { Text, View, Platform, TouchableWithoutFeedback } from 'react-native'; import { Component } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { withDispatch, withSelect } from '@wordpress/data'; -import { compose } from '@wordpress/compose'; +import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { createBlock, isUnmodifiedDefaultBlock } from '@wordpress/blocks'; -import { KeyboardAwareFlatList, ReadableContentView, withTheme } from '@wordpress/components'; +import { KeyboardAwareFlatList, ReadableContentView } from '@wordpress/components'; /** * Internal dependencies @@ -113,7 +113,7 @@ export class BlockList extends Component { } renderItem( { item: clientId, index } ) { - const blockHolderFocusedStyle = this.props.useStyle( styles.blockHolderFocused, styles.blockHolderFocusedDark ); + const blockHolderFocusedStyle = this.props.getStylesFromColorScheme( styles.blockHolderFocused, styles.blockHolderFocusedDark ); const { shouldShowBlockAtIndex, shouldShowInsertionPoint } = this.props; return ( <ReadableContentView> @@ -133,8 +133,8 @@ export class BlockList extends Component { } renderAddBlockSeparator() { - const lineStyle = this.props.useStyle( styles.lineStyleAddHere, styles.lineStyleAddHereDark ); - const labelStyle = this.props.useStyle( styles.labelStyleAddHere, styles.labelStyleAddHereDark ); + const lineStyle = this.props.getStylesFromColorScheme( styles.lineStyleAddHere, styles.lineStyleAddHereDark ); + const labelStyle = this.props.getStylesFromColorScheme( styles.labelStyleAddHere, styles.labelStyleAddHereDark ); return ( <View style={ styles.containerStyleAddHere } > <View style={ lineStyle }></View> @@ -216,6 +216,6 @@ export default compose( [ replaceBlock, }; } ), - withTheme, + withPreferredColorScheme, ] )( BlockList ); diff --git a/packages/block-editor/src/components/inserter/index.native.js b/packages/block-editor/src/components/inserter/index.native.js index b64c460f14bd67..4c06402af38214 100644 --- a/packages/block-editor/src/components/inserter/index.native.js +++ b/packages/block-editor/src/components/inserter/index.native.js @@ -2,10 +2,10 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { Dropdown, ToolbarButton, Dashicon, withTheme } from '@wordpress/components'; +import { Dropdown, ToolbarButton, Dashicon } from '@wordpress/components'; import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; -import { compose } from '@wordpress/compose'; +import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { getUnregisteredTypeHandlerName } from '@wordpress/blocks'; /** @@ -56,9 +56,9 @@ class Inserter extends Component { const { disabled, renderToggle = defaultRenderToggle, - useStyle, + getStylesFromColorScheme, } = this.props; - const style = useStyle( styles.addBlockButton, styles.addBlockButtonDark ); + const style = getStylesFromColorScheme( styles.addBlockButton, styles.addBlockButtonDark ); return renderToggle( { onToggle, isOpen, disabled, style } ); } @@ -119,5 +119,5 @@ export default compose( [ items: inserterItems.filter( ( { name } ) => name !== getUnregisteredTypeHandlerName() ), }; } ), - withTheme, + withPreferredColorScheme, ] )( Inserter ); diff --git a/packages/block-editor/src/components/inserter/menu.native.js b/packages/block-editor/src/components/inserter/menu.native.js index 9dbf9b5b162713..2a990445b284c4 100644 --- a/packages/block-editor/src/components/inserter/menu.native.js +++ b/packages/block-editor/src/components/inserter/menu.native.js @@ -13,8 +13,8 @@ import { isUnmodifiedDefaultBlock, } from '@wordpress/blocks'; import { withDispatch, withSelect } from '@wordpress/data'; -import { withInstanceId, compose } from '@wordpress/compose'; -import { BottomSheet, Icon, withTheme } from '@wordpress/components'; +import { withInstanceId, compose, withPreferredColorScheme } from '@wordpress/compose'; +import { BottomSheet, Icon } from '@wordpress/components'; /** * Internal dependencies @@ -61,12 +61,12 @@ export class InserterMenu extends Component { } render() { - const { useStyle } = this.props; + const { getStylesFromColorScheme } = this.props; const numberOfColumns = this.calculateNumberOfColumns(); const bottomPadding = styles.contentBottomPadding; - const modalIconWrapperStyle = useStyle( styles.modalIconWrapper, styles.modalIconWrapperDark ); - const modalIconStyle = useStyle( styles.modalIcon, styles.modalIconDark ); - const modalItemLabelStyle = useStyle( styles.modalItemLabel, styles.modalItemLabelDark ); + const modalIconWrapperStyle = getStylesFromColorScheme( styles.modalIconWrapper, styles.modalIconWrapperDark ); + const modalIconStyle = getStylesFromColorScheme( styles.modalIcon, styles.modalIconDark ); + const modalItemLabelStyle = getStylesFromColorScheme( styles.modalItemLabel, styles.modalItemLabelDark ); return ( <BottomSheet @@ -217,5 +217,5 @@ export default compose( }; } ), withInstanceId, - withTheme, + withPreferredColorScheme, )( InserterMenu ); diff --git a/packages/block-editor/src/components/media-placeholder/index.native.js b/packages/block-editor/src/components/media-placeholder/index.native.js index a9a7d50fce11fd..3670d6381c5c69 100644 --- a/packages/block-editor/src/components/media-placeholder/index.native.js +++ b/packages/block-editor/src/components/media-placeholder/index.native.js @@ -12,7 +12,8 @@ import { MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, } from '@wordpress/block-editor'; -import { Dashicon, withTheme } from '@wordpress/components'; +import { Dashicon } from '@wordpress/components'; +import { withPreferredColorScheme } from '@wordpress/compose'; /** * Internal dependencies @@ -27,7 +28,7 @@ function MediaPlaceholder( props ) { onSelect, isAppender, disableMediaButtons, - useStyle, + getStylesFromColorScheme, } = props; const isOneType = allowedTypes.length === 1; @@ -60,8 +61,8 @@ function MediaPlaceholder( props ) { accessibilityHint = __( 'Double tap to select a video' ); } - const emptyStateContainerStyle = useStyle( styles.emptyStateContainer, styles.emptyStateContainerDark ); - const emptyStateTitleStyle = useStyle( styles.emptyStateTitle, styles.emptyStateTitleDark ); + const emptyStateContainerStyle = getStylesFromColorScheme( styles.emptyStateContainer, styles.emptyStateContainerDark ); + const emptyStateTitleStyle = getStylesFromColorScheme( styles.emptyStateTitle, styles.emptyStateTitleDark ); const renderContent = () => { if ( isAppender === undefined || ! isAppender ) { @@ -129,4 +130,4 @@ function MediaPlaceholder( props ) { ); } -export default withTheme( MediaPlaceholder ); +export default withPreferredColorScheme( MediaPlaceholder ); diff --git a/packages/block-editor/src/components/warning/index.native.js b/packages/block-editor/src/components/warning/index.native.js index e6c13b5dd75a99..dc7e83d16509af 100644 --- a/packages/block-editor/src/components/warning/index.native.js +++ b/packages/block-editor/src/components/warning/index.native.js @@ -6,7 +6,8 @@ import { View, Text } from 'react-native'; /** * WordPress dependencies */ -import { Icon, withTheme } from '@wordpress/components'; +import { Icon } from '@wordpress/components'; +import { withPreferredColorScheme } from '@wordpress/compose'; import { normalizeIconObject } from '@wordpress/blocks'; /** @@ -14,15 +15,15 @@ import { normalizeIconObject } from '@wordpress/blocks'; */ import styles from './style.scss'; -function Warning( { title, message, icon, iconClass, theme, useStyle, ...viewProps } ) { +function Warning( { title, message, icon, iconClass, preferredColorScheme, getStylesFromColorScheme, ...viewProps } ) { icon = icon && normalizeIconObject( icon ); - const internalIconClass = 'warning-icon' + '-' + theme; - const titleStyle = useStyle( styles.title, styles.titleDark ); - const messageStyle = useStyle( styles.message, styles.messageDark ); + const internalIconClass = 'warning-icon' + '-' + preferredColorScheme; + const titleStyle = getStylesFromColorScheme( styles.title, styles.titleDark ); + const messageStyle = getStylesFromColorScheme( styles.message, styles.messageDark ); return ( <View - style={ useStyle( styles.container, styles.containerDark ) } + style={ getStylesFromColorScheme( styles.container, styles.containerDark ) } { ...viewProps } > { icon && ( @@ -43,4 +44,4 @@ function Warning( { title, message, icon, iconClass, theme, useStyle, ...viewPro ); } -export default withTheme( Warning ); +export default withPreferredColorScheme( Warning ); diff --git a/packages/block-library/src/code/edit.native.js b/packages/block-library/src/code/edit.native.js index a8afae8f0195b4..d8641b2270d20a 100644 --- a/packages/block-library/src/code/edit.native.js +++ b/packages/block-library/src/code/edit.native.js @@ -8,7 +8,7 @@ import { View } from 'react-native'; */ import { PlainText } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; -import { withTheme } from '@wordpress/components'; +import { withPreferredColorScheme } from '@wordpress/compose'; /** * Internal dependencies @@ -23,9 +23,9 @@ import styles from './theme.scss'; // Note: styling is applied directly to the (nested) PlainText component. Web-side components // apply it to the container 'div' but we don't have a proper proposal for cascading styling yet. export function CodeEdit( props ) { - const { attributes, setAttributes, style, onFocus, onBlur, useStyle } = props; - const codeStyle = useStyle( styles.blockCode, styles.blockCodeDark ); - const placeholderStyle = useStyle( styles.placeholder, styles.placeholderDark ); + const { attributes, setAttributes, style, onFocus, onBlur, getStylesFromColorScheme } = props; + const codeStyle = getStylesFromColorScheme( styles.blockCode, styles.blockCodeDark ); + const placeholderStyle = getStylesFromColorScheme( styles.placeholder, styles.placeholderDark ); return ( <View> @@ -47,4 +47,4 @@ export function CodeEdit( props ) { ); } -export default withTheme( CodeEdit ); +export default withPreferredColorScheme( CodeEdit ); diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index eb846520ab53f6..db437f11f1703a 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -19,7 +19,6 @@ import { Icon, Toolbar, ToolbarButton, - withTheme, } from '@wordpress/components'; import { @@ -33,6 +32,7 @@ import { import { __, sprintf } from '@wordpress/i18n'; import { isURL } from '@wordpress/url'; import { doAction, hasAction } from '@wordpress/hooks'; +import { withPreferredColorScheme } from '@wordpress/compose'; /** * Internal dependencies @@ -198,7 +198,7 @@ class ImageEdit extends React.Component { return <Icon icon={ SvgIconRetry } { ...styles.iconRetry } />; } - const iconStyle = this.props.useStyle( styles.icon, styles.iconDark ); + const iconStyle = this.props.getStylesFromColorScheme( styles.icon, styles.iconDark ); return <Icon icon={ SvgIcon } { ...iconStyle } />; } @@ -371,4 +371,4 @@ class ImageEdit extends React.Component { } } -export default withTheme( ImageEdit ); +export default withPreferredColorScheme( ImageEdit ); diff --git a/packages/block-library/src/missing/edit.native.js b/packages/block-library/src/missing/edit.native.js index 356aa68f0fb330..cd4898a4e8c692 100644 --- a/packages/block-library/src/missing/edit.native.js +++ b/packages/block-library/src/missing/edit.native.js @@ -6,7 +6,8 @@ import { View, Text } from 'react-native'; /** * WordPress dependencies */ -import { Icon, withTheme } from '@wordpress/components'; +import { Icon } from '@wordpress/components'; +import { withPreferredColorScheme } from '@wordpress/compose'; import { coreBlocks } from '@wordpress/block-library'; import { normalizeIconObject } from '@wordpress/blocks'; import { Component } from '@wordpress/element'; @@ -20,17 +21,17 @@ import styles from './style.scss'; export class UnsupportedBlockEdit extends Component { render() { const { originalName } = this.props.attributes; - const { useStyle, theme } = this.props; + const { getStylesFromColorScheme, preferredColorScheme } = this.props; const blockType = coreBlocks[ originalName ]; const title = blockType ? blockType.settings.title : __( 'Unsupported' ); - const titleStyle = useStyle( styles.unsupportedBlockMessage, styles.unsupportedBlockMessageDark ); + const titleStyle = getStylesFromColorScheme( styles.unsupportedBlockMessage, styles.unsupportedBlockMessageDark ); const icon = blockType ? normalizeIconObject( blockType.settings.icon ) : 'admin-plugins'; - const iconStyle = useStyle( styles.unsupportedBlockIcon, styles.unsupportedBlockIconDark ); - const iconClassName = 'unsupported-icon' + '-' + theme; + const iconStyle = getStylesFromColorScheme( styles.unsupportedBlockIcon, styles.unsupportedBlockIconDark ); + const iconClassName = 'unsupported-icon' + '-' + preferredColorScheme; return ( - <View style={ useStyle( styles.unsupportedBlock, styles.unsupportedBlockDark ) }> + <View style={ getStylesFromColorScheme( styles.unsupportedBlock, styles.unsupportedBlockDark ) }> <Icon className={ iconClassName } icon={ icon && icon.src ? icon.src : icon } color={ iconStyle.color } /> <Text style={ titleStyle }>{ title }</Text> </View> @@ -38,4 +39,4 @@ export class UnsupportedBlockEdit extends Component { } } -export default withTheme( UnsupportedBlockEdit ); +export default withPreferredColorScheme( UnsupportedBlockEdit ); diff --git a/packages/block-library/src/more/edit.native.js b/packages/block-library/src/more/edit.native.js index c266512b287831..88908d3f44837c 100644 --- a/packages/block-library/src/more/edit.native.js +++ b/packages/block-library/src/more/edit.native.js @@ -9,7 +9,7 @@ import Hr from 'react-native-hr'; */ import { __ } from '@wordpress/i18n'; import { Component } from '@wordpress/element'; -import { withTheme } from '@wordpress/components'; +import { withPreferredColorScheme } from '@wordpress/compose'; /** * Internal dependencies @@ -26,13 +26,13 @@ export class MoreEdit extends Component { } render() { - const { attributes, useStyle } = this.props; + const { attributes, getStylesFromColorScheme } = this.props; const { customText } = attributes; const { defaultText } = this.state; const content = customText || defaultText; - const textStyle = useStyle( styles.moreText, styles.moreTextDark ); - const lineStyle = useStyle( styles.moreLine, styles.moreLineDark ); + const textStyle = getStylesFromColorScheme( styles.moreText, styles.moreTextDark ); + const lineStyle = getStylesFromColorScheme( styles.moreLine, styles.moreLineDark ); return ( <View> @@ -48,4 +48,4 @@ export class MoreEdit extends Component { } } -export default withTheme( MoreEdit ); +export default withPreferredColorScheme( MoreEdit ); diff --git a/packages/block-library/src/nextpage/edit.native.js b/packages/block-library/src/nextpage/edit.native.js index 37f9c235b7ef6f..25591a00269cb7 100644 --- a/packages/block-library/src/nextpage/edit.native.js +++ b/packages/block-library/src/nextpage/edit.native.js @@ -8,19 +8,19 @@ import Hr from 'react-native-hr'; * WordPress dependencies */ import { __, sprintf } from '@wordpress/i18n'; -import { withTheme } from '@wordpress/components'; +import { withPreferredColorScheme } from '@wordpress/compose'; /** * Internal dependencies */ import styles from './editor.scss'; -export function NextPageEdit( { attributes, isSelected, onFocus, useStyle } ) { +export function NextPageEdit( { attributes, isSelected, onFocus, getStylesFromColorScheme } ) { const { customText = __( 'Page break' ) } = attributes; const accessibilityTitle = attributes.customText || ''; const accessibilityState = isSelected ? [ 'selected' ] : []; - const textStyle = useStyle( styles.nextpageText, styles.nextpageTextDark ); - const lineStyle = useStyle( styles.nextpageLine, styles.nextpageLineDark ); + const textStyle = getStylesFromColorScheme( styles.nextpageText, styles.nextpageTextDark ); + const lineStyle = getStylesFromColorScheme( styles.nextpageLine, styles.nextpageLineDark ); return ( <View @@ -44,4 +44,4 @@ export function NextPageEdit( { attributes, isSelected, onFocus, useStyle } ) { ); } -export default withTheme( NextPageEdit ); +export default withPreferredColorScheme( NextPageEdit ); diff --git a/packages/block-library/src/video/edit.native.js b/packages/block-library/src/video/edit.native.js index 39fc0e0d2233df..94dc83b3474426 100644 --- a/packages/block-library/src/video/edit.native.js +++ b/packages/block-library/src/video/edit.native.js @@ -20,9 +20,8 @@ import { Icon, Toolbar, ToolbarButton, - withTheme, } from '@wordpress/components'; - +import { withPreferredColorScheme } from '@wordpress/compose'; import { Caption, MediaPlaceholder, @@ -153,7 +152,7 @@ class VideoEdit extends React.Component { return <Icon icon={ SvgIconRetry } { ...style.icon } />; } - const iconStyle = this.props.useStyle( style.icon, style.iconDark ); + const iconStyle = this.props.getStylesFromColorScheme( style.icon, style.iconDark ); return <Icon icon={ SvgIcon } { ...( ! isMediaPlaceholder ? style.iconUploading : iconStyle ) } />; } @@ -265,4 +264,4 @@ class VideoEdit extends React.Component { } } -export default withTheme( VideoEdit ); +export default withPreferredColorScheme( VideoEdit ); diff --git a/packages/components/src/index.native.js b/packages/components/src/index.native.js index 209507ec96b440..a12d2e8baad21c 100644 --- a/packages/components/src/index.native.js +++ b/packages/components/src/index.native.js @@ -28,4 +28,3 @@ export { default as KeyboardAvoidingView } from './mobile/keyboard-avoiding-view export { default as KeyboardAwareFlatList } from './mobile/keyboard-aware-flat-list'; export { default as Picker } from './mobile/picker'; export { default as ReadableContentView } from './mobile/readable-content-view'; -export * from './mobile/dark-mode'; diff --git a/packages/components/src/mobile/bottom-sheet/cell.native.js b/packages/components/src/mobile/bottom-sheet/cell.native.js index 6a5fc6ba38947f..af74a25e2cee82 100644 --- a/packages/components/src/mobile/bottom-sheet/cell.native.js +++ b/packages/components/src/mobile/bottom-sheet/cell.native.js @@ -10,13 +10,13 @@ import { isEmpty } from 'lodash'; import { Dashicon } from '@wordpress/components'; import { Component } from '@wordpress/element'; import { __, _x, sprintf } from '@wordpress/i18n'; +import { withPreferredColorScheme } from '@wordpress/compose'; /** * Internal dependencies */ import styles from './styles.scss'; import platformStyles from './cellStyles.scss'; -import { withTheme } from '../dark-mode'; class BottomSheetCell extends Component { constructor( props ) { @@ -49,14 +49,14 @@ class BottomSheetCell extends Component { editable = true, separatorType, style = {}, - useStyle, + getStylesFromColorScheme, ...valueProps } = this.props; const showValue = value !== undefined; const isValueEditable = editable && onChangeValue !== undefined; - const cellLabelStyle = useStyle( styles.cellLabel, styles.cellTextDark ); - const cellLabelCenteredStyle = useStyle( styles.cellLabelCentered, styles.cellTextDark ); + const cellLabelStyle = getStylesFromColorScheme( styles.cellLabel, styles.cellTextDark ); + const cellLabelCenteredStyle = getStylesFromColorScheme( styles.cellLabelCentered, styles.cellTextDark ); const defaultLabelStyle = showValue || icon !== undefined ? cellLabelStyle : cellLabelCenteredStyle; const drawSeparator = ( separatorType && separatorType !== 'none' ) || separatorStyle === undefined; @@ -80,8 +80,8 @@ class BottomSheetCell extends Component { const separatorStyle = () => { //eslint-disable-next-line @wordpress/no-unused-vars-before-return - const defaultSeparatorStyle = this.props.useStyle( styles.separator, styles.separatorDark ); - const cellSeparatorStyle = this.props.useStyle( styles.cellSeparator, styles.cellSeparatorDark ); + const defaultSeparatorStyle = this.props.getStylesFromColorScheme( styles.separator, styles.separatorDark ); + const cellSeparatorStyle = this.props.getStylesFromColorScheme( styles.cellSeparator, styles.cellSeparatorDark ); const leftMarginStyle = { ...cellSeparatorStyle, ...platformStyles.separatorMarginLeft }; switch ( separatorType ) { case 'leftMargin': @@ -97,7 +97,7 @@ class BottomSheetCell extends Component { const getValueComponent = () => { const styleRTL = I18nManager.isRTL && styles.cellValueRTL; - const cellValueStyle = this.props.useStyle( styles.cellValue, styles.cellTextDark ); + const cellValueStyle = this.props.getStylesFromColorScheme( styles.cellValue, styles.cellTextDark ); const finalStyle = { ...cellValueStyle, ...valueStyle, ...styleRTL }; // To be able to show the `middle` ellipsizeMode on editable cells @@ -150,7 +150,7 @@ class BottomSheetCell extends Component { ); }; - const iconStyle = useStyle( styles.icon, styles.iconDark ); + const iconStyle = getStylesFromColorScheme( styles.icon, styles.iconDark ); return ( <TouchableOpacity @@ -188,4 +188,4 @@ class BottomSheetCell extends Component { } } -export default withTheme( BottomSheetCell ); +export default withPreferredColorScheme( BottomSheetCell ); diff --git a/packages/components/src/mobile/bottom-sheet/index.native.js b/packages/components/src/mobile/bottom-sheet/index.native.js index 805ddb084c0587..f17284b49e7306 100644 --- a/packages/components/src/mobile/bottom-sheet/index.native.js +++ b/packages/components/src/mobile/bottom-sheet/index.native.js @@ -9,6 +9,7 @@ import SafeArea from 'react-native-safe-area'; * WordPress dependencies */ import { Component } from '@wordpress/element'; +import { withPreferredColorScheme } from '@wordpress/compose'; /** * Internal dependencies @@ -19,7 +20,6 @@ import Cell from './cell'; import PickerCell from './picker-cell'; import SwitchCell from './switch-cell'; import KeyboardAvoidingView from './keyboard-avoiding-view'; -import { withTheme } from '../dark-mode'; class BottomSheet extends Component { constructor() { @@ -64,7 +64,7 @@ class BottomSheet extends Component { hideHeader, style = {}, contentStyle = {}, - useStyle, + getStylesFromColorScheme, } = this.props; const panResponder = PanResponder.create( { @@ -120,7 +120,7 @@ class BottomSheet extends Component { }, }; - const backgroundStyle = useStyle( styles.background, styles.backgroundDark ); + const backgroundStyle = getStylesFromColorScheme( styles.background, styles.backgroundDark ); return ( <Modal @@ -164,7 +164,7 @@ function getWidth() { return Math.min( Dimensions.get( 'window' ).width, styles.background.maxWidth ); } -const ThemedBottomSheet = withTheme( BottomSheet ); +const ThemedBottomSheet = withPreferredColorScheme( BottomSheet ); ThemedBottomSheet.getWidth = getWidth; ThemedBottomSheet.Button = Button; diff --git a/packages/components/src/mobile/dark-mode/index.native.js b/packages/components/src/mobile/dark-mode/index.native.js deleted file mode 100644 index 44ff4c38a8f644..00000000000000 --- a/packages/components/src/mobile/dark-mode/index.native.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * External dependencies - */ -import { eventEmitter, initialMode } from 'react-native-dark-mode'; -import React from 'react'; - -// Conditional needed to pass UI Tests on CI -if ( eventEmitter.setMaxListeners ) { - eventEmitter.setMaxListeners( 150 ); -} - -export function withTheme( WrappedComponent ) { - return class extends React.Component { - constructor( props ) { - super( props ); - - this.onModeChanged = this.onModeChanged.bind( this ); - this.useStyle = this.useStyle.bind( this ); - - this.state = { - mode: initialMode, - }; - } - - onModeChanged( newMode ) { - this.setState( { mode: newMode } ); - } - - componentDidMount() { - this.subscription = eventEmitter.on( 'currentModeChanged', this.onModeChanged ); - } - - componentWillUnmount() { - // Conditional needed to pass UI Tests on CI - if ( eventEmitter.removeListener ) { - eventEmitter.removeListener( 'currentModeChanged', this.onModeChanged ); - } - } - - useStyle( light, dark ) { - const isDarkMode = this.state.mode === 'dark'; - const finalDark = { - ...light, - ...dark, - }; - - return isDarkMode ? finalDark : light; - } - - render() { - return <WrappedComponent - theme={ this.state.mode } - useStyle={ this.useStyle } - { ...this.props } - />; - } - }; -} diff --git a/packages/components/src/mobile/html-text-input/index.native.js b/packages/components/src/mobile/html-text-input/index.native.js index 4032fa5a74b0d5..7b7c648216ef33 100644 --- a/packages/components/src/mobile/html-text-input/index.native.js +++ b/packages/components/src/mobile/html-text-input/index.native.js @@ -10,12 +10,11 @@ import { Component } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { parse } from '@wordpress/blocks'; import { withDispatch, withSelect } from '@wordpress/data'; -import { withInstanceId, compose } from '@wordpress/compose'; +import { withInstanceId, compose, withPreferredColorScheme } from '@wordpress/compose'; /** * Internal dependencies */ -import { withTheme } from '../dark-mode'; import HTMLInputContainer from './container'; import styles from './style.scss'; @@ -61,9 +60,9 @@ export class HTMLTextInput extends Component { } render() { - const { useStyle } = this.props; - const htmlStyle = useStyle( styles.htmlView, styles.htmlViewDark ); - const placeholderStyle = useStyle( styles.placeholder, styles.placeholderDark ); + const { getStylesFromColorScheme } = this.props; + const htmlStyle = getStylesFromColorScheme( styles.htmlView, styles.htmlViewDark ); + const placeholderStyle = getStylesFromColorScheme( styles.placeholder, styles.placeholderDark ); return ( <HTMLInputContainer parentHeight={ this.props.parentHeight }> <TextInput @@ -123,5 +122,5 @@ export default compose( [ }; } ), withInstanceId, - withTheme, + withPreferredColorScheme, ] )( HTMLTextInput ); diff --git a/packages/components/src/mobile/html-text-input/test/index.native.js b/packages/components/src/mobile/html-text-input/test/index.native.js index 4a10c0159293df..2481e533a4fad8 100644 --- a/packages/components/src/mobile/html-text-input/test/index.native.js +++ b/packages/components/src/mobile/html-text-input/test/index.native.js @@ -33,14 +33,14 @@ const findTitleTextInput = ( wrapper ) => { return findTextInputInWrapper( wrapper, { placeholder } ); }; -const useStyle = () => { +const getStylesFromColorScheme = () => { return { color: 'white' }; }; describe( 'HTMLTextInput', () => { it( 'HTMLTextInput renders', () => { const wrapper = shallow( - <HTMLTextInput useStyle={ useStyle } /> + <HTMLTextInput getStylesFromColorScheme={ getStylesFromColorScheme } /> ); expect( wrapper ).toBeTruthy(); } ); @@ -51,7 +51,7 @@ describe( 'HTMLTextInput', () => { const wrapper = shallow( <HTMLTextInput onChange={ onChange } - useStyle={ useStyle } + getStylesFromColorScheme={ getStylesFromColorScheme } /> ); @@ -76,7 +76,7 @@ describe( 'HTMLTextInput', () => { <HTMLTextInput onPersist={ onPersist } onChange={ jest.fn() } - useStyle={ useStyle } + getStylesFromColorScheme={ getStylesFromColorScheme } /> ); @@ -107,7 +107,7 @@ describe( 'HTMLTextInput', () => { const wrapper = shallow( <HTMLTextInput editTitle={ editTitle } - useStyle={ useStyle } + getStylesFromColorScheme={ getStylesFromColorScheme } /> ); diff --git a/packages/components/src/toolbar/toolbar-container.native.js b/packages/components/src/toolbar/toolbar-container.native.js index c92251c38c19cf..33fe77d11db4c0 100644 --- a/packages/components/src/toolbar/toolbar-container.native.js +++ b/packages/components/src/toolbar/toolbar-container.native.js @@ -3,16 +3,20 @@ */ import { View } from 'react-native'; +/** + * WordPress dependencies + */ +import { withPreferredColorScheme } from '@wordpress/compose'; + /** * Internal dependencies */ import styles from './style.scss'; -import { withTheme } from '../mobile/dark-mode'; -const ToolbarContainer = ( { useStyle, passedStyle, children } ) => ( - <View style={ [ useStyle( styles.container, styles.containerDark ), passedStyle ] }> +const ToolbarContainer = ( { getStylesFromColorScheme, passedStyle, children } ) => ( + <View style={ [ getStylesFromColorScheme( styles.container, styles.containerDark ), passedStyle ] }> { children } </View> ); -export default withTheme( ToolbarContainer ); +export default withPreferredColorScheme( ToolbarContainer ); diff --git a/packages/compose/src/higher-order/with-preferred-color-scheme/index.native.js b/packages/compose/src/higher-order/with-preferred-color-scheme/index.native.js new file mode 100644 index 00000000000000..3b393c2ecaf5c7 --- /dev/null +++ b/packages/compose/src/higher-order/with-preferred-color-scheme/index.native.js @@ -0,0 +1,32 @@ +/** + * Internal dependencies + */ +import createHigherOrderComponent from '../../utils/create-higher-order-component'; +import usePreferredColorScheme from '../../hooks/use-preferred-color-scheme'; + +const withPreferredColorScheme = createHigherOrderComponent( + ( WrappedComponent ) => ( props ) => { + const colorScheme = usePreferredColorScheme(); + const isDarkMode = colorScheme === 'dark'; + + const getStyles = ( lightStyles, darkStyles ) => { + const finalDarkStyles = { + ...lightStyles, + ...darkStyles, + }; + + return isDarkMode ? finalDarkStyles : lightStyles; + }; + + return ( + <WrappedComponent + preferredColorScheme={ colorScheme } + getStylesFromColorScheme={ getStyles } + { ...props } + /> + ); + }, + 'withPreferredColorScheme' +); + +export default withPreferredColorScheme; diff --git a/packages/compose/src/hooks/use-preferred-color-scheme/index.native.js b/packages/compose/src/hooks/use-preferred-color-scheme/index.native.js new file mode 100644 index 00000000000000..c748b6830e5167 --- /dev/null +++ b/packages/compose/src/hooks/use-preferred-color-scheme/index.native.js @@ -0,0 +1,13 @@ +/** + * External dependencies + */ +import { useDarkModeContext } from 'react-native-dark-mode'; + +/** + * Returns the color scheme value when it changes. Possible values: [ 'light', 'dark' ] + * + * @return {string} return current color scheme. + */ +const usePreferredColorScheme = useDarkModeContext; + +export default usePreferredColorScheme; diff --git a/packages/compose/src/index.native.js b/packages/compose/src/index.native.js new file mode 100644 index 00000000000000..f410b60094d005 --- /dev/null +++ b/packages/compose/src/index.native.js @@ -0,0 +1,9 @@ + +// Web exports +export * from './index.js'; + +// Higher-order components +export { default as withPreferredColorScheme } from './higher-order/with-preferred-color-scheme'; + +// Hooks +export { default as usePreferredColorScheme } from './hooks/use-preferred-color-scheme'; diff --git a/packages/edit-post/src/components/header/header-toolbar/index.native.js b/packages/edit-post/src/components/header/header-toolbar/index.native.js index 5da763a5dd8e5d..6f87cc7bfc7df5 100644 --- a/packages/edit-post/src/components/header/header-toolbar/index.native.js +++ b/packages/edit-post/src/components/header/header-toolbar/index.native.js @@ -7,7 +7,7 @@ import { ScrollView, View } from 'react-native'; /** * WordPress dependencies */ -import { compose } from '@wordpress/compose'; +import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { withSelect, withDispatch } from '@wordpress/data'; import { withViewportMatch } from '@wordpress/viewport'; import { __ } from '@wordpress/i18n'; @@ -15,7 +15,7 @@ import { Inserter, BlockToolbar, } from '@wordpress/block-editor'; -import { Toolbar, ToolbarButton, withTheme } from '@wordpress/components'; +import { Toolbar, ToolbarButton } from '@wordpress/components'; /** * Internal dependencies @@ -30,7 +30,7 @@ function HeaderToolbar( { undo, showInserter, showKeyboardHideButton, - useStyle, + getStylesFromColorScheme, onHideKeyboard, } ) { const scrollViewRef = useRef( null ); @@ -39,7 +39,7 @@ function HeaderToolbar( { }; return ( - <View style={ useStyle( styles.container, styles.containerDark ) }> + <View style={ getStylesFromColorScheme( styles.container, styles.containerDark ) }> <ScrollView ref={ scrollViewRef } onContentSizeChange={ scrollToStart } @@ -108,5 +108,5 @@ export default compose( [ }; } ), withViewportMatch( { isLargeViewport: 'medium' } ), - withTheme, + withPreferredColorScheme, ] )( HeaderToolbar ); diff --git a/packages/edit-post/src/components/layout/index.native.js b/packages/edit-post/src/components/layout/index.native.js index 44bef0e3bec51b..44d9a2d51899c4 100644 --- a/packages/edit-post/src/components/layout/index.native.js +++ b/packages/edit-post/src/components/layout/index.native.js @@ -10,8 +10,8 @@ import { sendNativeEditorDidLayout } from 'react-native-gutenberg-bridge'; */ import { Component } from '@wordpress/element'; import { withSelect } from '@wordpress/data'; -import { compose } from '@wordpress/compose'; -import { HTMLTextInput, KeyboardAvoidingView, ReadableContentView, withTheme } from '@wordpress/components'; +import { compose, withPreferredColorScheme } from '@wordpress/compose'; +import { HTMLTextInput, KeyboardAvoidingView, ReadableContentView } from '@wordpress/components'; import { AutosaveMonitor } from '@wordpress/editor'; /** @@ -100,7 +100,7 @@ class Layout extends Component { render() { const { mode, - useStyle, + getStylesFromColorScheme, } = this.props; const isHtmlView = mode === 'text'; @@ -116,9 +116,9 @@ class Layout extends Component { }; return ( - <SafeAreaView style={ useStyle( styles.container, styles.containerDark ) } onLayout={ this.onRootViewLayout }> + <SafeAreaView style={ getStylesFromColorScheme( styles.container, styles.containerDark ) } onLayout={ this.onRootViewLayout }> <AutosaveMonitor /> - <View style={ useStyle( styles.background, styles.backgroundDark ) }> + <View style={ getStylesFromColorScheme( styles.background, styles.backgroundDark ) }> { isHtmlView ? this.renderHTML() : this.renderVisual() } </View> <View style={ { flex: 0, flexBasis: marginBottom, height: marginBottom } } /> @@ -148,5 +148,5 @@ export default compose( [ mode: getEditorMode(), }; } ), - withTheme, + withPreferredColorScheme, ] )( Layout ); diff --git a/packages/edit-post/src/components/visual-editor/index.native.js b/packages/edit-post/src/components/visual-editor/index.native.js index 1d8c7dbc094aff..44d7c2902cef4a 100644 --- a/packages/edit-post/src/components/visual-editor/index.native.js +++ b/packages/edit-post/src/components/visual-editor/index.native.js @@ -4,10 +4,10 @@ import { Component } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { withDispatch, withSelect } from '@wordpress/data'; -import { compose } from '@wordpress/compose'; +import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { BlockList } from '@wordpress/block-editor'; import { PostTitle } from '@wordpress/editor'; -import { ReadableContentView, withTheme } from '@wordpress/components'; +import { ReadableContentView } from '@wordpress/components'; /** * Internal dependencies @@ -20,9 +20,9 @@ class VisualEditor extends Component { editTitle, setTitleRef, title, - useStyle, + getStylesFromColorScheme, } = this.props; - const blockHolderFocusedStyle = useStyle( styles.blockHolderFocused, styles.blockHolderFocusedDark ); + const blockHolderFocusedStyle = getStylesFromColorScheme( styles.blockHolderFocused, styles.blockHolderFocusedDark ); return ( <ReadableContentView> <PostTitle @@ -83,5 +83,5 @@ export default compose( [ }, }; } ), - withTheme, + withPreferredColorScheme, ] )( VisualEditor ); diff --git a/packages/format-library/src/link/test/modal.native.js b/packages/format-library/src/link/test/modal.native.js index 44153ea3a40185..9d84f9f903ffbe 100644 --- a/packages/format-library/src/link/test/modal.native.js +++ b/packages/format-library/src/link/test/modal.native.js @@ -53,8 +53,7 @@ describe( 'LinksUI', () => { // Simulate user typing on the URL Cell. const bottomSheet = wrapper.dive().find( 'BottomSheet' ).first(); - // withTheme is type "_class", we search for it and dive into BottomSheetCell - const cell = bottomSheet.dive().find( '_class' ).first().dive(); + const cell = bottomSheet.dive().find( 'WithPreferredColorScheme(BottomSheetCell)' ).first().dive(); cell.simulate( 'changeValue', 'wordpress.com' ); diff --git a/packages/rich-text/src/component/index.native.js b/packages/rich-text/src/component/index.native.js index 9c3e44440dfd5b..206680d3c87668 100644 --- a/packages/rich-text/src/component/index.native.js +++ b/packages/rich-text/src/component/index.native.js @@ -14,13 +14,12 @@ import memize from 'memize'; * WordPress dependencies */ import { Component } from '@wordpress/element'; -import { compose } from '@wordpress/compose'; +import { compose, withPreferredColorScheme } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; import { childrenBlock } from '@wordpress/blocks'; import { decodeEntities } from '@wordpress/html-entities'; import { BACKSPACE } from '@wordpress/keycodes'; import { isURL } from '@wordpress/url'; -import { withTheme } from '@wordpress/components'; /** * Internal dependencies @@ -771,7 +770,7 @@ export class RichText extends Component { style, __unstableIsSelected: isSelected, children, - useStyle, + getStylesFromColorScheme, } = this.props; const record = this.getRecord(); @@ -782,7 +781,7 @@ export class RichText extends Component { minHeight = style.minHeight; } - const placeholderStyle = useStyle( styles.richTextPlaceholder, styles.richTextPlaceholderDark ); + const placeholderStyle = getStylesFromColorScheme( styles.richTextPlaceholder, styles.richTextPlaceholderDark ); const { color: defaultPlaceholderTextColor, @@ -792,7 +791,7 @@ export class RichText extends Component { color: defaultColor, textDecorationColor: defaultTextDecorationColor, fontFamily: defaultFontFamily, - } = useStyle( styles.richText, styles.richTextDark ); + } = getStylesFromColorScheme( styles.richText, styles.richTextDark ); let selection = null; if ( this.needsSelectionUpdate ) { @@ -821,7 +820,7 @@ export class RichText extends Component { this.firedAfterTextChanged = false; } - const dynamicStyle = useStyle( style, styles.richTextDark ); + const dynamicStyle = getStylesFromColorScheme( style, styles.richTextDark ); return ( <View> @@ -884,5 +883,5 @@ export default compose( [ withSelect( ( select ) => ( { formatTypes: select( 'core/rich-text' ).getFormatTypes(), } ) ), - withTheme, + withPreferredColorScheme, ] )( RichText ); diff --git a/packages/rich-text/src/component/test/index.native.js b/packages/rich-text/src/component/test/index.native.js index c4ff0da2b2f415..6b2bc12f855ffa 100644 --- a/packages/rich-text/src/component/test/index.native.js +++ b/packages/rich-text/src/component/test/index.native.js @@ -8,7 +8,7 @@ import { shallow } from 'enzyme'; */ import { RichText } from '../index'; -const useStyle = () => { +const getStylesFromColorScheme = () => { return { color: 'white' }; }; @@ -44,7 +44,7 @@ describe( 'RichText Native', () => { } } formatTypes={ [] } onSelectionChange={ jest.fn() } - useStyle={ useStyle } + getStylesFromColorScheme={ getStylesFromColorScheme } /> ); const event = { diff --git a/test/native/setup.js b/test/native/setup.js index f34947ed9958c0..dd59faa5fe000a 100644 --- a/test/native/setup.js +++ b/test/native/setup.js @@ -24,10 +24,7 @@ jest.mock( 'react-native-gutenberg-bridge', () => { jest.mock( 'react-native-dark-mode', () => { return { - eventEmitter: { - on: jest.fn(), - }, - initialMode: 'light', + useDarkModeContext: () => 'light', }; } ); From 1c9b13375539788f9e1d45403ed3382a8a7859d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Est=C3=AAv=C3=A3o?= <sergioestevao@gmail.com> Date: Wed, 25 Sep 2019 22:30:59 +0100 Subject: [PATCH 24/31] Add missing heading levels to the UI (H4, H5, H6) (#17533) --- packages/block-library/src/heading/edit.native.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-library/src/heading/edit.native.js b/packages/block-library/src/heading/edit.native.js index 389791663b0541..356494ea933b38 100644 --- a/packages/block-library/src/heading/edit.native.js +++ b/packages/block-library/src/heading/edit.native.js @@ -28,7 +28,7 @@ const HeadingEdit = ( { <BlockControls> <HeadingToolbar minLevel={ 2 } - maxLevel={ 5 } + maxLevel={ 7 } selectedLevel={ attributes.level } onChange={ ( newLevel ) => setAttributes( { level: newLevel } ) } /> From df025a6fcd892a12d29a81776f09dc29d6ca8d01 Mon Sep 17 00:00:00 2001 From: Luke Walczak <lukasz.walczak.pwr@gmail.com> Date: Thu, 26 Sep 2019 11:11:53 +0200 Subject: [PATCH 25/31] Fix lint issue (#17598) --- .../src/components/media-placeholder/index.native.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/media-placeholder/index.native.js b/packages/block-editor/src/components/media-placeholder/index.native.js index 3670d6381c5c69..4c92ccab44b790 100644 --- a/packages/block-editor/src/components/media-placeholder/index.native.js +++ b/packages/block-editor/src/components/media-placeholder/index.native.js @@ -61,7 +61,6 @@ function MediaPlaceholder( props ) { accessibilityHint = __( 'Double tap to select a video' ); } - const emptyStateContainerStyle = getStylesFromColorScheme( styles.emptyStateContainer, styles.emptyStateContainerDark ); const emptyStateTitleStyle = getStylesFromColorScheme( styles.emptyStateTitle, styles.emptyStateTitleDark ); const renderContent = () => { @@ -95,6 +94,8 @@ function MediaPlaceholder( props ) { return null; } + const emptyStateContainerStyle = getStylesFromColorScheme( styles.emptyStateContainer, styles.emptyStateContainerDark ); + return ( <View style={ { flex: 1 } }> <MediaUpload From d8b0d8310c62986a67a5bf75f37169965b5b19ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Est=C3=AAv=C3=A3o?= <sergioestevao@gmail.com> Date: Thu, 26 Sep 2019 11:24:18 +0100 Subject: [PATCH 26/31] Fix list filter on paste for RN mobile. (#17550) * Fix method for RN mobile. * Use array.From instead of slice. * Remove comment and use Array.from directly * Convert from NodeList spreadable to Array.from * Fix lint errors. * Fix documentation examples to use Array.from * Add empty line. --- packages/blocks/src/api/raw-handling/list-reducer.js | 2 +- packages/blocks/src/api/validation.js | 8 ++++---- packages/components/src/disabled/test/index.js | 2 +- packages/components/src/draggable/index.js | 2 +- .../components/src/higher-order/navigate-regions/index.js | 2 +- packages/dom/src/focusable.js | 2 +- packages/edit-post/src/components/admin-notices/index.js | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/blocks/src/api/raw-handling/list-reducer.js b/packages/blocks/src/api/raw-handling/list-reducer.js index a5f079dcb24880..a5e85949aea62b 100644 --- a/packages/blocks/src/api/raw-handling/list-reducer.js +++ b/packages/blocks/src/api/raw-handling/list-reducer.js @@ -8,7 +8,7 @@ function isList( node ) { } function shallowTextContent( element ) { - return [ ...element.childNodes ] + return Array.from( element.childNodes ) .map( ( { nodeValue = '' } ) => nodeValue ) .join( '' ); } diff --git a/packages/blocks/src/api/validation.js b/packages/blocks/src/api/validation.js index 444830c47c5ac7..6c0b98b69725fb 100644 --- a/packages/blocks/src/api/validation.js +++ b/packages/blocks/src/api/validation.js @@ -50,7 +50,7 @@ const REGEXP_STYLE_URL_TYPE = /^url\s*\(['"\s]*(.*?)['"\s]*\)$/; * See: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes * Extracted from: https://html.spec.whatwg.org/multipage/indices.html#attributes-3 * - * Object.keys( [ ...document.querySelectorAll( '#attributes-1 > tbody > tr' ) ] + * Object.keys( Array.from( document.querySelectorAll( '#attributes-1 > tbody > tr' ) ) * .filter( ( tr ) => tr.lastChild.textContent.indexOf( 'Boolean attribute' ) !== -1 ) * .reduce( ( result, tr ) => Object.assign( result, { * [ tr.firstChild.textContent.trim() ]: true @@ -97,7 +97,7 @@ const BOOLEAN_ATTRIBUTES = [ * See: https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#enumerated-attribute * Extracted from: https://html.spec.whatwg.org/multipage/indices.html#attributes-3 * - * Object.keys( [ ...document.querySelectorAll( '#attributes-1 > tbody > tr' ) ] + * Object.keys( Array.from( document.querySelectorAll( '#attributes-1 > tbody > tr' ) ) * .filter( ( tr ) => /^("(.+?)";?\s*)+/.test( tr.lastChild.textContent.trim() ) ) * .reduce( ( result, tr ) => Object.assign( result, { * [ tr.firstChild.textContent.trim() ]: true @@ -164,9 +164,9 @@ const TEXT_NORMALIZATIONS = [ * Tested aginst "12.5 Named character references": * * ``` - * const references = [ ...document.querySelectorAll( + * const references = Array.from( document.querySelectorAll( * '#named-character-references-table tr[id^=entity-] td:first-child' - * ) ].map( ( code ) => code.textContent ) + * ) ).map( ( code ) => code.textContent ) * references.every( ( reference ) => /^[\da-z]+$/i.test( reference ) ) * ``` * diff --git a/packages/components/src/disabled/test/index.js b/packages/components/src/disabled/test/index.js index bd7c556163e4ec..54120a61a61bc9 100644 --- a/packages/components/src/disabled/test/index.js +++ b/packages/components/src/disabled/test/index.js @@ -25,7 +25,7 @@ jest.mock( '@wordpress/dom', () => { // In JSDOM, all elements have zero'd widths and height. // This is a metric for focusable's `isVisible`, so find // and apply an arbitrary non-zero width. - [ ...context.querySelectorAll( '*' ) ].forEach( ( element ) => { + Array.from( context.querySelectorAll( '*' ) ).forEach( ( element ) => { Object.defineProperties( element, { offsetWidth: { get: () => 1, diff --git a/packages/components/src/draggable/index.js b/packages/components/src/draggable/index.js index ac077468d83777..cd7d8c310e9530 100644 --- a/packages/components/src/draggable/index.js +++ b/packages/components/src/draggable/index.js @@ -116,7 +116,7 @@ class Draggable extends Component { } // Hack: Remove iFrames as it's causing the embeds drag clone to freeze - [ ...clone.querySelectorAll( 'iframe' ) ].forEach( ( child ) => child.parentNode.removeChild( child ) ); + Array.from( clone.querySelectorAll( 'iframe' ) ).forEach( ( child ) => child.parentNode.removeChild( child ) ); this.cloneWrapper.appendChild( clone ); elementWrapper.appendChild( this.cloneWrapper ); diff --git a/packages/components/src/higher-order/navigate-regions/index.js b/packages/components/src/higher-order/navigate-regions/index.js index c716e2d1ea9f14..b8b010a7414f45 100644 --- a/packages/components/src/higher-order/navigate-regions/index.js +++ b/packages/components/src/higher-order/navigate-regions/index.js @@ -34,7 +34,7 @@ export default createHigherOrderComponent( } focusRegion( offset ) { - const regions = [ ...this.container.querySelectorAll( '[role="region"]' ) ]; + const regions = Array.from( this.container.querySelectorAll( '[role="region"]' ) ); if ( ! regions.length ) { return; } diff --git a/packages/dom/src/focusable.js b/packages/dom/src/focusable.js index 133b89d3669747..12688755cfe445 100644 --- a/packages/dom/src/focusable.js +++ b/packages/dom/src/focusable.js @@ -76,7 +76,7 @@ function isValidFocusableArea( element ) { export function find( context ) { const elements = context.querySelectorAll( SELECTOR ); - return [ ...elements ].filter( ( element ) => { + return Array.from( elements ).filter( ( element ) => { if ( ! isVisible( element ) ) { return false; } diff --git a/packages/edit-post/src/components/admin-notices/index.js b/packages/edit-post/src/components/admin-notices/index.js index af04efaa9953a3..d39c74c34a0620 100644 --- a/packages/edit-post/src/components/admin-notices/index.js +++ b/packages/edit-post/src/components/admin-notices/index.js @@ -27,7 +27,7 @@ const NOTICE_CLASS_STATUSES = { function getAdminNotices() { // The order is reversed to match expectations of rendered order, since a // NoticesList is itself rendered in reverse order (newest to oldest). - return [ ...document.querySelectorAll( '#wpbody-content > .notice' ) ].reverse(); + return Array.from( document.querySelectorAll( '#wpbody-content > .notice' ) ).reverse(); } /** From f3085c1eb012d8a125fe683620a2d0a7a6d045a9 Mon Sep 17 00:00:00 2001 From: Gerardo Pacheco <gerardo.sicart@gmail.com> Date: Fri, 27 Sep 2019 09:55:44 +0200 Subject: [PATCH 27/31] [RNMobile] Move MediaUploadPorgress to its own component folder (#17392) * Move MediaUploadPorgress to its own component folder (native) * MediaUploadProgress - Fix import to code standards * MediaUploadProgress readme * Mobile - MediaUploadProgress README update --- .../src/components/index.native.js | 1 + .../media-upload-progress/README.md | 140 ++++++++++++++++++ .../image-size.native.js | 10 +- .../media-upload-progress/index.native.js} | 0 .../media-upload-progress/styles.native.scss | 4 + .../test/index.native.js} | 2 +- .../block-library/src/image/edit.native.js | 2 +- .../src/image/styles.native.scss | 5 - .../block-library/src/video/edit.native.js | 2 +- 9 files changed, 157 insertions(+), 9 deletions(-) create mode 100644 packages/block-editor/src/components/media-upload-progress/README.md rename packages/{block-library/src/image => block-editor/src/components/media-upload-progress}/image-size.native.js (83%) rename packages/{block-library/src/image/media-upload-progress.native.js => block-editor/src/components/media-upload-progress/index.native.js} (100%) create mode 100644 packages/block-editor/src/components/media-upload-progress/styles.native.scss rename packages/{block-library/src/image/test/media-upload-progress.native.js => block-editor/src/components/media-upload-progress/test/index.native.js} (99%) diff --git a/packages/block-editor/src/components/index.native.js b/packages/block-editor/src/components/index.native.js index 7c6906da0066a7..b45904361595db 100644 --- a/packages/block-editor/src/components/index.native.js +++ b/packages/block-editor/src/components/index.native.js @@ -18,6 +18,7 @@ export { } from './rich-text'; export { default as MediaPlaceholder } from './media-placeholder'; export { default as MediaUpload, MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO } from './media-upload'; +export { default as MediaUploadProgress } from './media-upload-progress'; export { default as URLInput } from './url-input'; export { default as BlockInvalidWarning } from './block-list/block-invalid-warning'; export { default as Caption } from './caption'; diff --git a/packages/block-editor/src/components/media-upload-progress/README.md b/packages/block-editor/src/components/media-upload-progress/README.md new file mode 100644 index 00000000000000..89ef9243717774 --- /dev/null +++ b/packages/block-editor/src/components/media-upload-progress/README.md @@ -0,0 +1,140 @@ +MediaUploadProgress +=================== + +`MediaUploadProgress` shows a progress bar while a media file associated with a block is being uploaded. + +## Usage + +Usage example + +```jsx +import { ImageBackground, Text, View } from 'react-native'; +import { + MediaUploadProgress, +} from '@wordpress/block-editor'; + +function MediaProgress( { height, width, url, id } ) { + return ( + <MediaUploadProgress + height={ height } + width={ width } + coverUrl={ url } + mediaId={ id } + renderContent={ ( { isUploadFailed, finalWidth, finalHeight, retryMessage } ) => { + return ( + <ImageBackground + style={ { width: finalWidth, height: finalHeight } } + resizeMethod="scale" + source={ { uri: url } } + > + { isUploadFailed && + <View> + <Text>{ retryMessage }</Text> + </View> + } + </ImageBackground> + ); + } } + /> + ); +} +``` + +## Props + +### mediaId + +A media ID that identifies the current upload. + +- Type: `Number` +- Required: Yes +- Platform: Mobile + +### coverUrl + +By passing an image url, it'll calculate the right size depending on the container of the component maintaining its aspect ratio, it'll pass these values through `renderContent`. + +- Type: `String` +- Required: No +- Platform: Mobile + +### renderContent + +Content to be rendered along with the progress bar, usually the thumbnail of the media being uploaded. + +- Type: `React components` +- Required: Yes +- Platform: Mobile + +It passes an object containing the following properties: + +**With** `coverUrl` as a parameter: + +`{ isUploadInProgress, isUploadFailed, finalWidth, finalHeight, imageWidthWithinContainer, retryMessage }` + +**Without** `coverUrl` as a parameter: + +`{ isUploadInProgress, isUploadFailed, retryMessage }` + + +### width + +Forces the final width to be returned in `finalWidth` + +- Type: `Number` +- Required: No +- Platform: Mobile + +### height + +Forces the final height to be returned in `finalHeight` + +- Type: `Number` +- Required: No +- Platform: Mobile + +### onUpdateMediaProgress + +Callback called when the progress of the upload is updated. + +- Type: `Function` +- Required: No +- Platform: Mobile + +The argument of the callback is an object containing the following properties: + +`{ mediaId, mediaUrl, progress, state }` + +### onFinishMediaUploadWithSuccess + +Callback called when the media file has been uploaded successfully. + +- Type: `Function` +- Required: No +- Platform: Mobile + +The argument of the callback is an object containing the following properties: + +`{ mediaId, mediaServerId, mediaUrl, progress, state }` + +### onFinishMediaUploadWithFailure + +Callback called when the media file couldn't be uploaded. + +- Type: `Function` +- Required: No +- Platform: Mobile + +The argument of the callback is an object containing the following properties: + +`{ mediaId, progress, state }` + + +### onMediaUploadStateReset + +Callback called when the media upload is reset + +- Type: `Function` +- Required: No +- Platform: Mobile + diff --git a/packages/block-library/src/image/image-size.native.js b/packages/block-editor/src/components/media-upload-progress/image-size.native.js similarity index 83% rename from packages/block-library/src/image/image-size.native.js rename to packages/block-editor/src/components/media-upload-progress/image-size.native.js index a337a5d14548c7..ade5984c1cadab 100644 --- a/packages/block-library/src/image/image-size.native.js +++ b/packages/block-editor/src/components/media-upload-progress/image-size.native.js @@ -11,7 +11,15 @@ import { View, Image } from 'react-native'; /** * Internal dependencies */ -import { calculatePreferedImageSize } from './utils'; + +function calculatePreferedImageSize( image, container ) { + const maxWidth = container.clientWidth; + const exceedMaxWidth = image.width > maxWidth; + const ratio = image.height / image.width; + const width = exceedMaxWidth ? maxWidth : image.width; + const height = exceedMaxWidth ? maxWidth * ratio : image.height; + return { width, height }; +} class ImageSize extends Component { constructor() { diff --git a/packages/block-library/src/image/media-upload-progress.native.js b/packages/block-editor/src/components/media-upload-progress/index.native.js similarity index 100% rename from packages/block-library/src/image/media-upload-progress.native.js rename to packages/block-editor/src/components/media-upload-progress/index.native.js diff --git a/packages/block-editor/src/components/media-upload-progress/styles.native.scss b/packages/block-editor/src/components/media-upload-progress/styles.native.scss new file mode 100644 index 00000000000000..5dea1caaf5aeff --- /dev/null +++ b/packages/block-editor/src/components/media-upload-progress/styles.native.scss @@ -0,0 +1,4 @@ +.mediaUploadProgress { + flex: 1; + background-color: $gray-lighten-30; +} diff --git a/packages/block-library/src/image/test/media-upload-progress.native.js b/packages/block-editor/src/components/media-upload-progress/test/index.native.js similarity index 99% rename from packages/block-library/src/image/test/media-upload-progress.native.js rename to packages/block-editor/src/components/media-upload-progress/test/index.native.js index 042f571aeefe58..e7c48e80d05df5 100644 --- a/packages/block-library/src/image/test/media-upload-progress.native.js +++ b/packages/block-editor/src/components/media-upload-progress/test/index.native.js @@ -15,7 +15,7 @@ import { MEDIA_UPLOAD_STATE_SUCCEEDED, MEDIA_UPLOAD_STATE_FAILED, MEDIA_UPLOAD_STATE_RESET, -} from '../media-upload-progress'; +} from '../'; jest.mock( 'react-native-gutenberg-bridge', () => { const callUploadCallback = ( payload ) => { diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index db437f11f1703a..1839c19c0a58d2 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -25,6 +25,7 @@ import { Caption, MediaPlaceholder, MediaUpload, + MediaUploadProgress, MEDIA_TYPE_IMAGE, BlockControls, InspectorControls, @@ -38,7 +39,6 @@ import { withPreferredColorScheme } from '@wordpress/compose'; * Internal dependencies */ import styles from './styles.scss'; -import MediaUploadProgress from './media-upload-progress'; import SvgIcon from './icon'; import SvgIconRetry from './icon-retry'; diff --git a/packages/block-library/src/image/styles.native.scss b/packages/block-library/src/image/styles.native.scss index aaafc3ae5457c8..9c5a9cbf5c45e5 100644 --- a/packages/block-library/src/image/styles.native.scss +++ b/packages/block-library/src/image/styles.native.scss @@ -22,11 +22,6 @@ color: $alert-red; } -.mediaUploadProgress { - flex: 1; - background-color: $gray-lighten-30; -} - .modalIcon { width: 80px; height: 80px; diff --git a/packages/block-library/src/video/edit.native.js b/packages/block-library/src/video/edit.native.js index 94dc83b3474426..c69ae7bb39e8e2 100644 --- a/packages/block-library/src/video/edit.native.js +++ b/packages/block-library/src/video/edit.native.js @@ -26,6 +26,7 @@ import { Caption, MediaPlaceholder, MediaUpload, + MediaUploadProgress, MEDIA_TYPE_VIDEO, BlockControls, InspectorControls, @@ -37,7 +38,6 @@ import { doAction, hasAction } from '@wordpress/hooks'; /** * Internal dependencies */ -import MediaUploadProgress from '../image/media-upload-progress'; import style from './style.scss'; import SvgIcon from './icon'; import SvgIconRetry from './icon-retry'; From 95acf23ad5cbf5ce7e62e9d62b8ee7c8eafef8aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Est=C3=AAv=C3=A3o?= <sergioestevao@gmail.com> Date: Mon, 30 Sep 2019 13:39:42 +0100 Subject: [PATCH 28/31] Rnmobile/fix link editing on start (#17631) * Don't try to clear links if text is clean. * Commented LinkUI removal test when no URL. * Don't try to remove link if we are at start of link and no actual selection is --- packages/format-library/src/link/index.native.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/format-library/src/link/index.native.js b/packages/format-library/src/link/index.native.js index 8457682d07d285..c9640b62de77bf 100644 --- a/packages/format-library/src/link/index.native.js +++ b/packages/format-library/src/link/index.native.js @@ -94,7 +94,15 @@ export const link = { } onRemoveFormat() { - const { onChange, speak } = this.props; + const { onChange, speak, value } = this.props; + const startFormat = getActiveFormat( value, 'core/link' ); + + // If the previous position from start doesn't have the link is because we are at the start + const linkStart = ! find( value.formats[ value.start - 1 ], startFormat ); + if ( linkStart && isCollapsed( value ) ) { + return; + } + const linkSelection = this.getLinkSelection(); onChange( removeFormat( linkSelection, name ) ); From cae51b44536b00cfa3de6f1ad31a407cb463c890 Mon Sep 17 00:00:00 2001 From: Cameron Voell <cameronvoell@gmail.com> Date: Tue, 22 Oct 2019 20:14:00 -0700 Subject: [PATCH 29/31] Re-implementing #17802, affected by merge. Removing extra style. --- .../src/components/media-placeholder/styles.native.scss | 4 ---- packages/rich-text/src/component/index.native.js | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/media-placeholder/styles.native.scss b/packages/block-editor/src/components/media-placeholder/styles.native.scss index a0b7445debf66b..03c7c73b2edfc9 100644 --- a/packages/block-editor/src/components/media-placeholder/styles.native.scss +++ b/packages/block-editor/src/components/media-placeholder/styles.native.scss @@ -19,10 +19,6 @@ background-color: $background-dark-secondary; } -.emptyStateContainerDark { - background-color: $background-dark-secondary; -} - .emptyStateTitle { text-align: center; margin-top: 8; diff --git a/packages/rich-text/src/component/index.native.js b/packages/rich-text/src/component/index.native.js index 63a0be642cb63d..09ac451ed56d23 100644 --- a/packages/rich-text/src/component/index.native.js +++ b/packages/rich-text/src/component/index.native.js @@ -826,8 +826,6 @@ export class RichText extends Component { this.firedAfterTextChanged = false; } - const dynamicStyle = getStylesFromColorScheme( style, styles.richTextDark ); - return ( <View> { children && children( { @@ -844,7 +842,7 @@ export class RichText extends Component { } } } style={ { - ...dynamicStyle, + ...style, minHeight: Math.max( minHeight, this.state.height ), } } text={ { text: html, eventCount: this.lastEventCount, selection } } From a927609367bed31fd0c7074ee78d36ad9bbc3e1b Mon Sep 17 00:00:00 2001 From: Cameron Voell <cameronvoell@gmail.com> Date: Sat, 2 Nov 2019 10:32:31 -0700 Subject: [PATCH 30/31] Fullscreen image preview enabled for ios only --- packages/block-library/src/image/edit.native.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index b19655c70d0c27..8bcd1537d04d6a 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -2,7 +2,7 @@ * External dependencies */ import React from 'react'; -import { View, ImageBackground, Text, TouchableWithoutFeedback, Dimensions } from 'react-native'; +import { View, ImageBackground, Text, TouchableWithoutFeedback, Dimensions, Platform } from 'react-native'; import { requestMediaImport, mediaUploadSync, @@ -142,7 +142,7 @@ export class ImageEdit extends React.Component { requestImageFailedRetryDialog( attributes.id ); } // eslint-disable-next-line no-undef - const enableFullscreen = __DEV__; + const enableFullscreen = Platform.OS === 'ios'; this.setState( { isCaptionSelected: false, showImageViewer: enableFullscreen && true, From c259edf9d85905a564927c634860470cddafeb58 Mon Sep 17 00:00:00 2001 From: Cameron Voell <cameronvoell@gmail.com> Date: Sun, 3 Nov 2019 22:27:04 -0800 Subject: [PATCH 31/31] Removing no longer needed eslint disable --- packages/block-library/src/image/edit.native.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/block-library/src/image/edit.native.js b/packages/block-library/src/image/edit.native.js index 8bcd1537d04d6a..0f2448e64dc2cd 100644 --- a/packages/block-library/src/image/edit.native.js +++ b/packages/block-library/src/image/edit.native.js @@ -141,7 +141,6 @@ export class ImageEdit extends React.Component { } else if ( attributes.id && ! isURL( attributes.url ) ) { requestImageFailedRetryDialog( attributes.id ); } - // eslint-disable-next-line no-undef const enableFullscreen = Platform.OS === 'ios'; this.setState( { isCaptionSelected: false,