Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improves RTL style conversion #20503

Merged
merged 8 commits into from
Mar 12, 2020
Merged

Improves RTL style conversion #20503

merged 8 commits into from
Mar 12, 2020

Conversation

ItsJonQ
Copy link

@ItsJonQ ItsJonQ commented Feb 27, 2020

Description

This update improves the rtl style utility to better handle varying combinations of left and Left based properties. A handful of tests have been added to ensure style property conversions are outputted as expected.

Types of changes

  • Adjusted the regex logic for left and Left replacements in the rtl convertor

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR.

Addresses (🤞) @aduth 's comments from https://github.com/WordPress/gutenberg/pull/19916/files/110831f8f61f5e43ff9c76331159415040b412c9#r379631860

@github-actions
Copy link

github-actions bot commented Feb 27, 2020

Size Change: +64 B (0%)

Total Size: 866 kB

Filename Size Change
build/components/index.js 191 kB +64 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.01 kB 0 B
build/annotations/index.js 3.43 kB 0 B
build/api-fetch/index.js 3.39 kB 0 B
build/autop/index.js 2.58 kB 0 B
build/blob/index.js 620 B 0 B
build/block-directory/index.js 6.02 kB 0 B
build/block-directory/style-rtl.css 760 B 0 B
build/block-directory/style.css 760 B 0 B
build/block-editor/index.js 104 kB 0 B
build/block-editor/style-rtl.css 10.5 kB 0 B
build/block-editor/style.css 10.5 kB 0 B
build/block-library/editor-rtl.css 7.66 kB 0 B
build/block-library/editor.css 7.66 kB 0 B
build/block-library/index.js 116 kB 0 B
build/block-library/style-rtl.css 7.49 kB 0 B
build/block-library/style.css 7.5 kB 0 B
build/block-library/theme-rtl.css 669 B 0 B
build/block-library/theme.css 671 B 0 B
build/block-serialization-default-parser/index.js 1.65 kB 0 B
build/block-serialization-spec-parser/index.js 3.1 kB 0 B
build/blocks/index.js 57.6 kB 0 B
build/components/style-rtl.css 15.5 kB 0 B
build/components/style.css 15.5 kB 0 B
build/compose/index.js 5.76 kB 0 B
build/core-data/index.js 10.5 kB 0 B
build/data-controls/index.js 1.03 kB 0 B
build/data/index.js 8.22 kB 0 B
build/date/index.js 5.37 kB 0 B
build/deprecated/index.js 772 B 0 B
build/dom-ready/index.js 568 B 0 B
build/dom/index.js 3.06 kB 0 B
build/edit-post/index.js 90.9 kB 0 B
build/edit-post/style-rtl.css 8.54 kB 0 B
build/edit-post/style.css 8.54 kB 0 B
build/edit-site/index.js 4.63 kB 0 B
build/edit-site/style-rtl.css 2.51 kB 0 B
build/edit-site/style.css 2.51 kB 0 B
build/edit-widgets/index.js 4.42 kB 0 B
build/edit-widgets/style-rtl.css 2.59 kB 0 B
build/edit-widgets/style.css 2.58 kB 0 B
build/editor/editor-styles-rtl.css 325 B 0 B
build/editor/editor-styles.css 327 B 0 B
build/editor/index.js 44.6 kB 0 B
build/editor/style-rtl.css 4.01 kB 0 B
build/editor/style.css 4 kB 0 B
build/element/index.js 4.45 kB 0 B
build/escape-html/index.js 733 B 0 B
build/format-library/index.js 7.6 kB 0 B
build/format-library/style-rtl.css 502 B 0 B
build/format-library/style.css 502 B 0 B
build/hooks/index.js 1.92 kB 0 B
build/html-entities/index.js 622 B 0 B
build/i18n/index.js 3.48 kB 0 B
build/is-shallow-equal/index.js 710 B 0 B
build/keyboard-shortcuts/index.js 2.3 kB 0 B
build/keycodes/index.js 1.68 kB 0 B
build/list-reusable-blocks/index.js 2.99 kB 0 B
build/list-reusable-blocks/style-rtl.css 226 B 0 B
build/list-reusable-blocks/style.css 226 B 0 B
build/media-utils/index.js 4.85 kB 0 B
build/notices/index.js 1.57 kB 0 B
build/nux/index.js 3.02 kB 0 B
build/nux/style-rtl.css 616 B 0 B
build/nux/style.css 613 B 0 B
build/plugins/index.js 2.54 kB 0 B
build/primitives/index.js 1.49 kB 0 B
build/priority-queue/index.js 780 B 0 B
build/redux-routine/index.js 2.84 kB 0 B
build/rich-text/index.js 14.3 kB 0 B
build/server-side-render/index.js 2.54 kB 0 B
build/shortcode/index.js 1.7 kB 0 B
build/token-list/index.js 1.27 kB 0 B
build/url/index.js 4 kB 0 B
build/viewport/index.js 1.61 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.18 kB 0 B

compressed-size-action

Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't remark on it originally, but shouldn't we be switching instances of "right" to "left" as well?

@ItsJonQ
Copy link
Author

ItsJonQ commented Feb 28, 2020

@aduth Ah yes! Thank you so much <3. I really appreciate your thoroughness and feedback! I guess I got a little tunnel-visioned on just the left -> right aspect of it.

I just made an update and added tests for left -> right, and right -> left.

@aduth
Copy link
Member

aduth commented Feb 28, 2020

Now I can add the edge case test I had planned to add yesterday 😄 See. 474455450 (currently failing)

Related: https://developer.mozilla.org/en-US/docs/Web/CSS/text-combine-upright

@ItsJonQ
Copy link
Author

ItsJonQ commented Feb 28, 2020

@aduth TIL about text-combine-upright 😱

@aduth
Copy link
Member

aduth commented Feb 28, 2020

@ItsJonQ I had never heard of it previously either 😄 Just pulled it as an example looking through a full list of CSS properties for substrings of "left" and "right" which we'd not expect to flip in the sense of horizontal directionality.

Comment on lines 43 to 45
if ( lowerLeftRegExp.test( key ) ) {
nextKey = key.replace( lowerLeftRegExp, '-right' );
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per my reference comment, would there be any difference if this was implemented as:

Suggested change
if ( lowerLeftRegExp.test( key ) ) {
nextKey = key.replace( lowerLeftRegExp, '-right' );
}
nextKey = key.replace( lowerLeftRegExp, '-right' );

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aduth I just tried. It breaks one of the conversions in the unit tests

  convertLtrToRtl
    ✓ converts (*)Left <-> (*)Right (6ms)
    ✕ converts (*)left <-> (*)right (7ms)

  ● convertLtrToRtl › converts (*)left <-> (*)right

    expect(received).toBe(expected) // Object.is equality

    Expected: 10
    Received: 20

      105 | 		expect( nextStyle[ 'scroll-margin-right' ] ).toBe( 10 );
      106 | 		expect( nextStyle[ 'scroll-padding-right' ] ).toBe( 10 );
    > 107 | 		expect( nextStyle.right ).toBe( 10 );
          | 		                          ^
      108 |
      109 | 		// right -> left
      110 | 		expect( nextStyle[ 'border-left' ] ).toBe( '20px solid blue' );

      at Object.toBe (packages/components/src/utils/test/rtl.js:107:29)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reason is that we should be doing the replace as operating on the working copy of nextKey.

Suggested change
if ( lowerLeftRegExp.test( key ) ) {
nextKey = key.replace( lowerLeftRegExp, '-right' );
}
nextKey = nextKey.replace( lowerLeftRegExp, '-right' );

Comment on lines 6 to 9
const lowerLeftRegExp = new RegExp( /-left/g );
const lowerRightRegExp = new RegExp( /-right/g );
const upperLeftRegExp = new RegExp( /Left/g );
const upperRightRegExp = new RegExp( /Right/g );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They look like constants to me.

https://developer.wordpress.org/coding-standards/wordpress-coding-standards/javascript/#constants

Suggested change
const lowerLeftRegExp = new RegExp( /-left/g );
const lowerRightRegExp = new RegExp( /-right/g );
const upperLeftRegExp = new RegExp( /Left/g );
const upperRightRegExp = new RegExp( /Right/g );
const LOWER_LEFT_REGEXP = new RegExp( /-left/g );
const LOWER_RIGHT_REGEXP = new RegExp( /-right/g );
const UPPER_LEFT_REGEXP = new RegExp( /Left/g );
const UPPER_RIGHT_REGEXP = new RegExp( /Right/g );

Comment on lines 43 to 45
if ( lowerLeftRegExp.test( key ) ) {
nextKey = key.replace( lowerLeftRegExp, '-right' );
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the reason is that we should be doing the replace as operating on the working copy of nextKey.

Suggested change
if ( lowerLeftRegExp.test( key ) ) {
nextKey = key.replace( lowerLeftRegExp, '-right' );
}
nextKey = nextKey.replace( lowerLeftRegExp, '-right' );

@aduth
Copy link
Member

aduth commented Feb 28, 2020

More a minor point, but could become a scaling issue: We should be able to stop immediately once we reach a match of any of these conditions, rather than continue to test them. It would probably be a refactor to a separate function using return:

function getConvertedKey( key ) {
	if ( key === 'left' ) {
		return 'right';
	}
	
	if ( key === 'right' ) {
		return 'left';
	}

	return key
		.replace( LOWER_LEFT_REGEXP, '-right' )
		.replace( LOWER_RIGHT_REGEXP, '-left' )
		.replace( UPPER_LEFT_REGEXP, 'Right' )
		.replace( UPPER_RIGHT_REGEXP, 'Left' );
}

Though now that I write this out, it only really benefits the direct 'right' and 'left', unless we keep the RegExp#test.

@aduth
Copy link
Member

aduth commented Feb 28, 2020

Actually, in testing that, I see now the problem with always running replace on the working copy of the key (rather than the original) is that a later replace will undo the effects of an earlier.

So a correct optimized version would be:

function getConvertedKey( key ) {
	if ( key === 'left' ) {
		return 'right';
	}

	if ( key === 'right' ) {
		return 'left';
	}

	if ( LOWER_LEFT_REGEXP.test( key ) ) {
		return key.replace( LOWER_LEFT_REGEXP, '-right' );
	}

	if ( LOWER_RIGHT_REGEXP.test( key ) ) {
		return key.replace( LOWER_RIGHT_REGEXP, '-left' );
	}

	if ( UPPER_LEFT_REGEXP.test( key ) ) {
		return key.replace( UPPER_LEFT_REGEXP, 'Right' );
	}

	if ( UPPER_RIGHT_REGEXP.test( key ) ) {
		return key.replace( UPPER_RIGHT_REGEXP, 'Left' );
	}

	return key;
}

@ItsJonQ
Copy link
Author

ItsJonQ commented Feb 28, 2020

running replace on the working copy of the key (rather than the original) is that a later replace will undo the effects of an earlier.

@aduth That's what I ran into as well 😂 . The chained replace would work if there were unique temporary tokens injected, which would be converted to desired values (at the very end).

Your refactor suggestion is great! I'll make the update

@aduth
Copy link
Member

aduth commented Feb 28, 2020

Another possible simplification:

diff --git a/packages/components/src/utils/rtl.js b/packages/components/src/utils/rtl.js
index 317d8eb4a..cf366412e 100644
--- a/packages/components/src/utils/rtl.js
+++ b/packages/components/src/utils/rtl.js
@@ -2,6 +2,7 @@
  * External dependencies
  */
 import { css } from '@emotion/core';
+import { mapKeys } from 'lodash';
 
 const LOWER_LEFT_REGEXP = new RegExp( /-left/g );
 const LOWER_RIGHT_REGEXP = new RegExp( /-right/g );
@@ -68,17 +69,8 @@ function getConvertedKey( key ) {
  *
  * @return {Object} Converted ltr -> rtl styles
  */
-export const convertLtrToRtl = ( ltrStyles = {} ) => {
-	const nextStyles = {};
-
-	for ( const key in ltrStyles ) {
-		const value = ltrStyles[ key ];
-		const nextKey = getConvertedKey( key );
-		nextStyles[ nextKey ] = value;
-	}
-
-	return nextStyles;
-};
+export const convertLtrToRtl = ( ltrStyles ) =>
+	mapKeys( ltrStyles, ( _value, key ) => getConvertedKey( key ) );
 
 /**
  * A higher-order function that create an incredibly basic ltr -> rtl style converter for CSS objects.

https://lodash.com/docs/4.17.15#mapKeys

Comment on lines 23 to 71
const convertLtrToRtl = ( ltrStyles = {} ) => {
export const convertLtrToRtl = ( ltrStyles = {} ) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the topic of case standards, there's also:

https://developer.wordpress.org/coding-standards/wordpress-coding-standards/javascript/#abbreviations-and-acronyms

i.e. This should technically be convertLTRToRTL

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!! Interesting question... In that case, should the main function be renamed from rtl() to RTL()?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rtl would be the correct capitalization. The phrasing is a bit strange, but it's meant to be covered by this bit of the standard:

If an abbreviation or an acronym occurs at the start of a variable name, it must be written to respect the camelcase naming rules covering the first letter of a variable or class definition. For variable assignment, this means writing the abbreviation entirely as lowercase.

@ItsJonQ
Copy link
Author

ItsJonQ commented Feb 28, 2020

Hmm.. not sure how to re-run Travis. I clicked "Re-run" on this page, but it didn't seem to do anything

@aduth
Copy link
Member

aduth commented Feb 28, 2020

@ItsJonQ I've been encountering the same in my pull request at #20544. Not sure yet if it might be an outage from where we download Chromium (for Puppeteer / E2E tests).

Jon Q and others added 6 commits February 28, 2020 18:22
This update improves the rtl style utility to better handle varying combinations of `left` and `Left` based properties. A handful of tests have been added to ensure style property conversions are outputted as expected.
@ItsJonQ ItsJonQ merged commit 7906556 into master Mar 12, 2020
@ItsJonQ ItsJonQ deleted the fix/css-js-rtl branch March 12, 2020 16:55
@github-actions github-actions bot added this to the Gutenberg 7.8 milestone Mar 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Components /packages/components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants