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

[RNMobile] Adjust Range-Control to add the stepper control and allow type selection #21465

Merged
merged 8 commits into from
Apr 15, 2020

Conversation

chipsnyder
Copy link
Contributor

@chipsnyder chipsnyder commented Apr 7, 2020

Fixes: wordpress-mobile/gutenberg-mobile#1992
gutenberg-mobile wordpress-mobile/gutenberg-mobile#2125

Description

This PR refactors the RangeControl component to allow the optional parameter type to allow selection between the Stepper control or a slider control on Mobile.

Since the range control is being used in multiple places right now as a slider control, I made the slider the default option.

This PR also updates the gallery component to pass in type=stepper to test the other use flow. The gallery component was also the only place where I could find references to the stepper control.

How has this been tested?

Stepper:

  1. Open a gallery block with multiple images
  2. Expect to see the stepper control under the columns option

Slider:

  1. Open a control using the RangeControl. Examples:

    • Button: Border Radius
    • Cover Block: Minimum height in pixels, Background opacity
    • Latest-Posts: Number of items, Excerpt length
  2. Expect to see the slider control

Screenshots

Types of changes

New feature adds type to RangeControl

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.

@chipsnyder chipsnyder added the Mobile App - i.e. Android or iOS Native mobile impl of the block editor. (Note: used in scripts, ping mobile folks to change) label Apr 7, 2020
@chipsnyder chipsnyder added this to the Future milestone Apr 7, 2020
@chipsnyder chipsnyder requested a review from geriux April 7, 2020 18:03
@github-actions
Copy link

github-actions bot commented Apr 7, 2020

Size Change: +1.77 kB (0%)

Total Size: 905 kB

Filename Size Change
build/block-library/index.js 112 kB -44 B (0%)
build/edit-widgets/style-rtl.css 4.65 kB +908 B (19%) ⚠️
build/edit-widgets/style.css 4.64 kB +909 B (19%) ⚠️
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.02 kB 0 B
build/annotations/index.js 3.62 kB 0 B
build/api-fetch/index.js 4.01 kB 0 B
build/autop/index.js 2.82 kB 0 B
build/blob/index.js 620 B 0 B
build/block-directory/index.js 6.24 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.2 kB 0 B
build/block-editor/style.css 10.2 kB 0 B
build/block-library/editor-rtl.css 7.11 kB 0 B
build/block-library/editor.css 7.11 kB 0 B
build/block-library/style-rtl.css 7.15 kB 0 B
build/block-library/style.css 7.16 kB 0 B
build/block-library/theme-rtl.css 683 B 0 B
build/block-library/theme.css 685 B 0 B
build/block-serialization-default-parser/index.js 1.88 kB 0 B
build/block-serialization-spec-parser/index.js 3.1 kB 0 B
build/blocks/index.js 57.7 kB 0 B
build/components/index.js 198 kB 0 B
build/components/style-rtl.css 16.6 kB 0 B
build/components/style.css 16.6 kB 0 B
build/compose/index.js 6.66 kB 0 B
build/core-data/index.js 11.1 kB 0 B
build/data-controls/index.js 1.25 kB 0 B
build/data/index.js 8.43 kB 0 B
build/date/index.js 5.47 kB 0 B
build/deprecated/index.js 772 B 0 B
build/dom-ready/index.js 569 B 0 B
build/dom/index.js 3.1 kB 0 B
build/edit-navigation/index.js 3.1 kB 0 B
build/edit-navigation/style-rtl.css 279 B 0 B
build/edit-navigation/style.css 280 B 0 B
build/edit-post/index.js 93.5 kB 0 B
build/edit-post/style-rtl.css 12.3 kB 0 B
build/edit-post/style.css 12.3 kB 0 B
build/edit-site/index.js 10.4 kB 0 B
build/edit-site/style-rtl.css 5.02 kB 0 B
build/edit-site/style.css 5.02 kB 0 B
build/edit-widgets/index.js 7.53 kB 0 B
build/editor/editor-styles-rtl.css 428 B 0 B
build/editor/editor-styles.css 431 B 0 B
build/editor/index.js 43.6 kB 0 B
build/editor/style-rtl.css 3.49 kB 0 B
build/editor/style.css 3.49 kB 0 B
build/element/index.js 4.64 kB 0 B
build/escape-html/index.js 733 B 0 B
build/format-library/index.js 7.29 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 2.13 kB 0 B
build/html-entities/index.js 622 B 0 B
build/i18n/index.js 3.56 kB 0 B
build/is-shallow-equal/index.js 711 B 0 B
build/keyboard-shortcuts/index.js 2.51 kB 0 B
build/keycodes/index.js 1.91 kB 0 B
build/list-reusable-blocks/index.js 3.12 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 5.28 kB 0 B
build/notices/index.js 1.79 kB 0 B
build/nux/index.js 3.4 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.67 kB 0 B
build/primitives/index.js 1.49 kB 0 B
build/priority-queue/index.js 789 B 0 B
build/redux-routine/index.js 2.84 kB 0 B
build/rich-text/index.js 14.8 kB 0 B
build/server-side-render/index.js 2.67 kB 0 B
build/shortcode/index.js 1.7 kB 0 B
build/token-list/index.js 1.28 kB 0 B
build/url/index.js 4.01 kB 0 B
build/viewport/index.js 1.84 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.17 kB 0 B

compressed-size-action

@geriux
Copy link
Member

geriux commented Apr 8, 2020

Thanks for taking this task @chipsnyder !

I think we are going in the right direction except for some things, I'll add them as a list but please let me know if some don't make any sense, it's what I've gathered from the discussions related to this component.

  • Inside the Gallery's edit.js we need to bring back the RangeControl component instead of using ColumnsControl which is something I added to avoid some import issues. After that, we could delete ColumnsControl.

  • Then as you already did, add the type prop in the native implementation of RangeControl.

  • Remove StepperControl which basically just imports StepperCell, this import would be moved to here so instead of importing StepperControl we'll import StepperCell as we do with RangeCell

  • And finally, in the RangeControl import within the Gallery block, we would add the type=stepper prop if the platform is mobile.

As I said before let me know if it makes sense or not 😅

@chipsnyder
Copy link
Contributor Author

Thanks @geriux for the review. This is a great background on when these controls were added and why. I made those suggested changes and it's ready for your next review!

label={ __( 'Columns' ) }
value={ columns }
onChange={ this.setColumnsNumber }
min={ 1 }
max={ Math.min( MAX_COLUMNS, images.length ) }
{ ...MOBILE_CONTROL_PROPS }
type="stepper"
Copy link
Member

Choose a reason for hiding this comment

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

This prop is for mobile only so what if we move it to MOBILE_CONTROL_PROPS? I know that it will be removed in the future but until then it looks like it's a good place, what do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My initial thoughts were adding it to MOBILE_CONTROL_PROPS might be confusing since it only applies to RangeControl. But you're right this might not be the best place for it because it will probably have the same problem as seperatorType. I can move that up to MOBILE_CONTROL_PROPS with some comments on why its there

Copy link
Member

Choose a reason for hiding this comment

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

Oh right! Totally missed that is being used for the ToggleControl as well. So I'm guessing we could use the Platform check inline, if it's native, pass down the prop.

Copy link
Contributor

Choose a reason for hiding this comment

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

it will probably have the same problem as seperatorType

Actually, we are "lucky" here, in that the web implementation passes the props down to <InputRange> but explicitly sets type after the spread props:

.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey @geriux

I failed to address @mkevins concerns in his comment here. (Sorry again about that Matt 😅 So just wanted to make sure you were in the loop as well.

Here is part of our convo from Slack that I think picks up the conversation well

Hey Chip 👋 😄
So, for some context: #20231 (comment)
There is actually some concern voiced over the use of this pattern with the MOBILE_CONTROL_PROPS platform deviating variable (which I introduced). This was introduced to deal with some platform differences that are cumbersome to reconcile. In the case for the type prop, I think it'd be best if we worked with web to develop a standard way to deal with "platform specific" props for a shared component. I.e. maybe the ideal solution is that we add this prop to web RangeControl to officially consume it, so it's not part of the ...props rest parameter when passed on to children. In the meantime, IMO it'd probably be best to limit the use of that pattern to where it's needed.
There is a lint-ignore that web has used a bit in the past for consuming unused props (heavily used in RichText, irrc), and now I'm wondering whether we can make this nicer.. perhaps via destructured rename with a particular prefix.. :thinking_face:

I think there are two parts here. What should we do for this case (I would be happy to make any changes). Then also what should we do long term for these patterns.

Focusing on the first part "What should we do for this case?" @geriux what do you think about @mkevins concerns?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I do agree that MOBILE_CONTROL_PROPS isn't the best approach, we did make an improvement already by removing the StepperControl and moving the rendering logic to the RangeControl (mobile).

Actually, we are "lucky" here, in that the web implementation passes the props down to but explicitly sets type after the spread props:

I talked about this with Chip and we also saw that we could just pass type and it wouldn't affect Web's side when rendering the component but it'd be weird if it was just being used by the mobile version of the component.

What should we do for this case?

I agree with Matt to add type to both RangeControl but I'm not sure how the web component could benefit from this prop. Maybe use already existing props? For example, I saw there's withInputField and the stepper doesn't have an input field since it's for smaller values. Maybe instead of having type=stepper we could pass withInputField=false (for mobile) to render the Stepper instead of the Slider?

Copy link
Contributor

@pinarol pinarol Apr 21, 2020

Choose a reason for hiding this comment

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

stepper doesn't have an input field since it's for smaller values

That rule is kinda outdated. Columns have now no limit for column count and it is controlled by stepper. Plus, our top priority should be keeping the component api meaningful and clear. If I were to use this component as a block author it wouldn't be clear to me that "withInputField=false" renders a stepper. I think the best way is to keep the "type" prop but document it as mobile only for now(which we already did) and if web folks would like to utilize it they can use it to render a web version of the stepper as well. There are also some props that are web only. I think the best way is just ignoring/discarding the mobile specific prop inside the web implementation. Otherwise, needing to filter passed props via platform is not a good development experience for the block author and it is quite easy to miss.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have a strong opinion short-term, but in general I see three options:

  1. Continue using this MOBILE_CONTROL_PROPS pattern until a longer-term solution lands
  2. Use the Platform.select function directly within the jsx
  3. Rely on the "luck" that type is currently being overwritten after the ...props rest parameter

The third option feels the most risky, since I don't know how likely it is the type prop would be moved / removed in the parameter list.

Longer term, it seems we may encounter a common situation whenever we have a cross-platform component with a few "platform-specific" props. One way to handle this is to explicitly name the unused prop, which removes it from the rest parameter. This then also requires a lint-ignore line, and usually an explanation for the lint-ignore (e.g. this is used in ClipboardButton component).

The eslint rule no-unused-vars has an option for ignoring rest parameter siblings, which is nice, but likely too crude for this purpose (since it would affect other unused props). There is also a convenient argsIgnorePattern option which allows us to ignore variables that match a specified RegEx.

Perhaps we can utilize this along with a destructuring rename, to create a pattern that makes it clear which props are ignored as platform specific, while also being removed from the rest parameter, and without the need for too many lint-ignore messages.

Example patch
diff --git a/.eslintrc.js b/.eslintrc.js
index a4a1ec153e..194e484307 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -121,6 +121,10 @@ module.exports = {
 					'Avoid truthy checks on length property rendering, as zero length is rendered verbatim.',
 			},
 		],
+		'no-unused-vars': [
+			'error',
+			{ argsIgnorePattern: '^__mobileOnlyProp' },
+		],
 	},
 	overrides: [
 		{
diff --git a/packages/block-library/src/gallery/edit.js b/packages/block-library/src/gallery/edit.js
index 3f77212f71..0cc920819c 100644
--- a/packages/block-library/src/gallery/edit.js
+++ b/packages/block-library/src/gallery/edit.js
@@ -65,11 +65,6 @@ const MOBILE_CONTROL_PROPS_SEPARATOR_NONE = Platform.select( {
 	native: { separatorType: 'none' },
 } );
 
-const MOBILE_CONTROL_PROPS_RANGE_CONTROL = Platform.select( {
-	web: {},
-	native: { type: 'stepper' },
-} );
-
 class GalleryEdit extends Component {
 	constructor() {
 		super( ...arguments );
@@ -403,7 +398,7 @@ class GalleryEdit extends Component {
 								min={ 1 }
 								max={ Math.min( MAX_COLUMNS, images.length ) }
 								{ ...MOBILE_CONTROL_PROPS }
-								{ ...MOBILE_CONTROL_PROPS_RANGE_CONTROL }
+								type="stepper"
 								required
 							/>
 						) }
diff --git a/packages/components/src/range-control/index.js b/packages/components/src/range-control/index.js
index d718dec1a2..c9dccefffd 100644
--- a/packages/components/src/range-control/index.js
+++ b/packages/components/src/range-control/index.js
@@ -64,6 +64,7 @@ const BaseRangeControl = forwardRef(
 			renderTooltipContent = ( v ) => v,
 			showTooltip: showTooltipProp,
 			step = 1,
+			type: __mobileOnlyPropType,
 			value: valueProp,
 			withInputField = true,
 			...props

This pattern works, but it still isn't perfectly clear that it is a mobile-only prop where it is used. Instead, it's only clear once visiting the component code. A way to potentially resolve that is to forgo the destructuring rename, and add a prefix to the prop itself to denote it's platform specificity: __mobileOnlyPropType="stepper". The same lint-ignore pattern could be used, and it would be clear in both the implementation and the usage of the component that the prop in question was a platform specific one.

In the solution which uses the prefixed variable directly, the platform specific implementation that actually uses it could use destructuring rename to aid readability for consuming code:

// my-component.native.js
const MyComponent = ( {
  __mobileOnlyPropSomeProp: someProp,
  someOtherProp,
  ...props
} ) => (
  someProp === 'someValue'
    ? <SomeChild { ...props } />
    : <SomeOtherChild { ...props } />
);

I would love to hear some other thoughts about this pattern as well, or other ideas / patterns for how to bring clarity to platform differences in our shared code. :)

Copy link
Member

Choose a reason for hiding this comment

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

Thank you for giving more detailed options @mkevins!

I think first I'd try to go with option 3:

Rely on the "luck" that type is currently being overwritten after the ...props rest parameter

As @pinarol mentioned, it is in the docs that it is a mobile-only prop. But I know it's easy to miss by only looking at the code.

I really like those patterns you suggested though, maybe it is worth investigating for future implementations that are mobile-only and make it some sort of a standard in the code.

@geriux
Copy link
Member

geriux commented Apr 13, 2020

Thanks @geriux for the review. This is a great background on when these controls were added and why. I made those suggested changes and it's ready for your next review!

Hey @chipsnyder! thanks for the changes! I commented on some small things but it's looking good!

@chipsnyder
Copy link
Contributor Author

Great feedback on those comments @geriux, those changes are done now.

Copy link
Member

@geriux geriux left a comment

Choose a reason for hiding this comment

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

LGTM! Thank you for all the changes and for working on this @chipsnyder!

Tested on both iOS, Android and local instance of the web editor. ✅

@chipsnyder chipsnyder merged commit f4cfa26 into master Apr 15, 2020
@chipsnyder chipsnyder deleted the rnmobile/issue/1992-RangeControl branch April 15, 2020 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Mobile App - i.e. Android or iOS Native mobile impl of the block editor. (Note: used in scripts, ping mobile folks to change)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Refactor RangeControl to render Stepper or Slider inside
4 participants