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

NumberControl: tidy up and fix types around value prop #45982

Open
wants to merge 17 commits into
base: trunk
Choose a base branch
from

Conversation

ciampo
Copy link
Contributor

@ciampo ciampo commented Nov 22, 2022

What?

Refactor internals of NumberControl with the goal of improving its types (ie. remove @ts-expect-error comments) and set the ground for the deprecation of the number type for the value prop.

Why?

See #45949 for more details about the work on NumberControl

How?

I recommend reviewing this PR commit by commit.

  • largely refactored / moved code around in order to de-tangle it and allow TypeScript not to error
  • made some runtime changes, especially around converting value-related variables from/to string (this also fixed TypeScript errors)
  • added unit tests to certify some utils behaviour

Question: I'm not sure about how to represent / convert falsey values ('', undefined, null, NaN...) between string and number, since some of them have "special" meanings:

  • undefined tells the component to switch to uncontrolled mode
  • '' therefore is used to tell the component that the value is empty, but in controlled mode
  • NaN is a valid number that could be returned when parsing strings — should we guard explicitly against it? And how?

Testing Instructions

Test NumberControl and its derived components (UnitControl, RangeControl, ColorPicker, AnglePickerControl...) both in Storybook and across the editor (mostly in the post and site editor sidebars).

Pay particular attention to:

  • how the component validates / clamps values in a variety of conditions with respect to trunk:
    • ie. when changing value of the isRequired prop
    • ie. when enabling isPressEnterToChange
    • when deleting a value by pressing backspace, and then blurring the input field
    • when entering an invalid value (i.e. lower than min) and then pressing enter and / or blurring the input field
  • controlled and uncontrolled behaviour with respect to what's on trunk (I've also added a specific story for the uncontrolled version of the component)

The goal of this refactor is to improve the code without introducing runtime changes / regressions. The only known runtime change (bug fix) is that the onChange callback in this PR always has a string as its argument (while on trunk that would sometimes be a number)

@codesandbox
Copy link

codesandbox bot commented Nov 22, 2022

CodeSandbox logoCodeSandbox logo  Open in CodeSandbox Web Editor | VS Code | VS Code Insiders

@github-actions
Copy link

github-actions bot commented Nov 22, 2022

Size Change: +261 B (0%)

Total Size: 1.33 MB

Filename Size Change
build/block-editor/content-rtl.css 3.65 kB +41 B (+1%)
build/block-editor/content.css 3.65 kB +42 B (+1%)
build/block-editor/index.min.js 184 kB +8 B (0%)
build/block-editor/style-rtl.css 14.2 kB -19 B (0%)
build/block-editor/style.css 14.2 kB -20 B (0%)
build/blocks/index.min.js 50.4 kB -5 B (0%)
build/components/index.min.js 203 kB +188 B (0%)
build/core-data/index.min.js 15.9 kB +21 B (0%)
build/edit-post/index.min.js 34.4 kB -3 B (0%)
build/editor/index.min.js 44.1 kB +8 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 993 B
build/annotations/index.min.js 2.78 kB
build/api-fetch/index.min.js 2.27 kB
build/autop/index.min.js 2.15 kB
build/blob/index.min.js 483 B
build/block-directory/index.min.js 7.16 kB
build/block-directory/style-rtl.css 1.04 kB
build/block-directory/style.css 1.04 kB
build/block-editor/default-editor-styles-rtl.css 403 B
build/block-editor/default-editor-styles.css 403 B
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 122 B
build/block-library/blocks/audio/style.css 122 B
build/block-library/blocks/audio/theme-rtl.css 138 B
build/block-library/blocks/audio/theme.css 138 B
build/block-library/blocks/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/style-rtl.css 84 B
build/block-library/blocks/avatar/style.css 84 B
build/block-library/blocks/block/editor-rtl.css 305 B
build/block-library/blocks/block/editor.css 305 B
build/block-library/blocks/button/editor-rtl.css 485 B
build/block-library/blocks/button/editor.css 485 B
build/block-library/blocks/button/style-rtl.css 532 B
build/block-library/blocks/button/style.css 532 B
build/block-library/blocks/buttons/editor-rtl.css 337 B
build/block-library/blocks/buttons/editor.css 337 B
build/block-library/blocks/buttons/style-rtl.css 332 B
build/block-library/blocks/buttons/style.css 332 B
build/block-library/blocks/calendar/style-rtl.css 239 B
build/block-library/blocks/calendar/style.css 239 B
build/block-library/blocks/categories/editor-rtl.css 84 B
build/block-library/blocks/categories/editor.css 83 B
build/block-library/blocks/categories/style-rtl.css 100 B
build/block-library/blocks/categories/style.css 100 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 406 B
build/block-library/blocks/columns/style.css 406 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-content/style-rtl.css 92 B
build/block-library/blocks/comment-content/style.css 92 B
build/block-library/blocks/comment-template/style-rtl.css 199 B
build/block-library/blocks/comment-template/style.css 198 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 840 B
build/block-library/blocks/comments/editor.css 839 B
build/block-library/blocks/comments/style-rtl.css 637 B
build/block-library/blocks/comments/style.css 636 B
build/block-library/blocks/cover/editor-rtl.css 612 B
build/block-library/blocks/cover/editor.css 613 B
build/block-library/blocks/cover/style-rtl.css 1.57 kB
build/block-library/blocks/cover/style.css 1.56 kB
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 B
build/block-library/blocks/embed/style-rtl.css 410 B
build/block-library/blocks/embed/style.css 410 B
build/block-library/blocks/embed/theme-rtl.css 138 B
build/block-library/blocks/embed/theme.css 138 B
build/block-library/blocks/file/editor-rtl.css 300 B
build/block-library/blocks/file/editor.css 300 B
build/block-library/blocks/file/style-rtl.css 253 B
build/block-library/blocks/file/style.css 254 B
build/block-library/blocks/file/view.min.js 353 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB
build/block-library/blocks/freeform/editor.css 2.44 kB
build/block-library/blocks/gallery/editor-rtl.css 984 B
build/block-library/blocks/gallery/editor.css 988 B
build/block-library/blocks/gallery/style-rtl.css 1.55 kB
build/block-library/blocks/gallery/style.css 1.55 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 654 B
build/block-library/blocks/group/editor.css 654 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 76 B
build/block-library/blocks/heading/style.css 76 B
build/block-library/blocks/html/editor-rtl.css 332 B
build/block-library/blocks/html/editor.css 333 B
build/block-library/blocks/image/editor-rtl.css 829 B
build/block-library/blocks/image/editor.css 828 B
build/block-library/blocks/image/style-rtl.css 627 B
build/block-library/blocks/image/style.css 630 B
build/block-library/blocks/image/theme-rtl.css 137 B
build/block-library/blocks/image/theme.css 137 B
build/block-library/blocks/latest-comments/style-rtl.css 298 B
build/block-library/blocks/latest-comments/style.css 298 B
build/block-library/blocks/latest-posts/editor-rtl.css 213 B
build/block-library/blocks/latest-posts/editor.css 212 B
build/block-library/blocks/latest-posts/style-rtl.css 478 B
build/block-library/blocks/latest-posts/style.css 478 B
build/block-library/blocks/list/style-rtl.css 88 B
build/block-library/blocks/list/style.css 88 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 507 B
build/block-library/blocks/media-text/style.css 505 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 716 B
build/block-library/blocks/navigation-link/editor.css 715 B
build/block-library/blocks/navigation-link/style-rtl.css 115 B
build/block-library/blocks/navigation-link/style.css 115 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation/editor-rtl.css 2.13 kB
build/block-library/blocks/navigation/editor.css 2.14 kB
build/block-library/blocks/navigation/style-rtl.css 2.22 kB
build/block-library/blocks/navigation/style.css 2.2 kB
build/block-library/blocks/navigation/view-modal.min.js 2.81 kB
build/block-library/blocks/navigation/view.min.js 447 B
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 376 B
build/block-library/blocks/page-list/editor.css 376 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 174 B
build/block-library/blocks/paragraph/editor.css 174 B
build/block-library/blocks/paragraph/style-rtl.css 279 B
build/block-library/blocks/paragraph/style.css 281 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/style-rtl.css 501 B
build/block-library/blocks/post-comments-form/style.css 501 B
build/block-library/blocks/post-date/style-rtl.css 61 B
build/block-library/blocks/post-date/style.css 61 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B
build/block-library/blocks/post-excerpt/editor.css 73 B
build/block-library/blocks/post-excerpt/style-rtl.css 69 B
build/block-library/blocks/post-excerpt/style.css 69 B
build/block-library/blocks/post-featured-image/editor-rtl.css 586 B
build/block-library/blocks/post-featured-image/editor.css 584 B
build/block-library/blocks/post-featured-image/style-rtl.css 318 B
build/block-library/blocks/post-featured-image/style.css 318 B
build/block-library/blocks/post-navigation-link/style-rtl.css 153 B
build/block-library/blocks/post-navigation-link/style.css 153 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 282 B
build/block-library/blocks/post-template/style.css 282 B
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-title/style-rtl.css 100 B
build/block-library/blocks/post-title/style.css 100 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 135 B
build/block-library/blocks/pullquote/editor.css 135 B
build/block-library/blocks/pullquote/style-rtl.css 326 B
build/block-library/blocks/pullquote/style.css 325 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 288 B
build/block-library/blocks/query-pagination/style.css 284 B
build/block-library/blocks/query-title/style-rtl.css 63 B
build/block-library/blocks/query-title/style.css 63 B
build/block-library/blocks/query/editor-rtl.css 440 B
build/block-library/blocks/query/editor.css 440 B
build/block-library/blocks/quote/style-rtl.css 213 B
build/block-library/blocks/quote/style.css 213 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 132 B
build/block-library/blocks/read-more/style.css 132 B
build/block-library/blocks/rss/editor-rtl.css 202 B
build/block-library/blocks/rss/editor.css 204 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 165 B
build/block-library/blocks/search/editor.css 165 B
build/block-library/blocks/search/style-rtl.css 409 B
build/block-library/blocks/search/style.css 406 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/separator/editor-rtl.css 146 B
build/block-library/blocks/separator/editor.css 146 B
build/block-library/blocks/separator/style-rtl.css 234 B
build/block-library/blocks/separator/style.css 234 B
build/block-library/blocks/separator/theme-rtl.css 194 B
build/block-library/blocks/separator/theme.css 194 B
build/block-library/blocks/shortcode/editor-rtl.css 474 B
build/block-library/blocks/shortcode/editor.css 474 B
build/block-library/blocks/site-logo/editor-rtl.css 490 B
build/block-library/blocks/site-logo/editor.css 490 B
build/block-library/blocks/site-logo/style-rtl.css 203 B
build/block-library/blocks/site-logo/style.css 203 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 116 B
build/block-library/blocks/site-title/editor.css 116 B
build/block-library/blocks/site-title/style-rtl.css 57 B
build/block-library/blocks/site-title/style.css 57 B
build/block-library/blocks/social-link/editor-rtl.css 184 B
build/block-library/blocks/social-link/editor.css 184 B
build/block-library/blocks/social-links/editor-rtl.css 674 B
build/block-library/blocks/social-links/editor.css 673 B
build/block-library/blocks/social-links/style-rtl.css 1.4 kB
build/block-library/blocks/social-links/style.css 1.39 kB
build/block-library/blocks/spacer/editor-rtl.css 332 B
build/block-library/blocks/spacer/editor.css 332 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 457 B
build/block-library/blocks/table/editor.css 457 B
build/block-library/blocks/table/style-rtl.css 651 B
build/block-library/blocks/table/style.css 650 B
build/block-library/blocks/table/theme-rtl.css 157 B
build/block-library/blocks/table/theme.css 157 B
build/block-library/blocks/tag-cloud/style-rtl.css 251 B
build/block-library/blocks/tag-cloud/style.css 253 B
build/block-library/blocks/template-part/editor-rtl.css 404 B
build/block-library/blocks/template-part/editor.css 404 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 99 B
build/block-library/blocks/verse/style.css 99 B
build/block-library/blocks/video/editor-rtl.css 691 B
build/block-library/blocks/video/editor.css 694 B
build/block-library/blocks/video/style-rtl.css 179 B
build/block-library/blocks/video/style.css 179 B
build/block-library/blocks/video/theme-rtl.css 139 B
build/block-library/blocks/video/theme.css 139 B
build/block-library/classic-rtl.css 162 B
build/block-library/classic.css 162 B
build/block-library/common-rtl.css 1.05 kB
build/block-library/common.css 1.05 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 11.7 kB
build/block-library/editor.css 11.7 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/index.min.js 199 kB
build/block-library/reset-rtl.css 478 B
build/block-library/reset.css 478 B
build/block-library/style-rtl.css 12.4 kB
build/block-library/style.css 12.4 kB
build/block-library/theme-rtl.css 698 B
build/block-library/theme.css 703 B
build/block-serialization-default-parser/index.min.js 1.13 kB
build/block-serialization-spec-parser/index.min.js 2.83 kB
build/components/style-rtl.css 11.6 kB
build/components/style.css 11.6 kB
build/compose/index.min.js 12.3 kB
build/customize-widgets/index.min.js 11.7 kB
build/customize-widgets/style-rtl.css 1.41 kB
build/customize-widgets/style.css 1.41 kB
build/data-controls/index.min.js 663 B
build/data/index.min.js 7.69 kB
build/date/index.min.js 32.1 kB
build/deprecated/index.min.js 518 B
build/dom-ready/index.min.js 336 B
build/dom/index.min.js 4.71 kB
build/edit-navigation/index.min.js 16.2 kB
build/edit-navigation/style-rtl.css 4.12 kB
build/edit-navigation/style.css 4.13 kB
build/edit-post/classic-rtl.css 571 B
build/edit-post/classic.css 571 B
build/edit-post/style-rtl.css 7.45 kB
build/edit-post/style.css 7.44 kB
build/edit-site/index.min.js 66.5 kB
build/edit-site/style-rtl.css 9.36 kB
build/edit-site/style.css 9.36 kB
build/edit-widgets/index.min.js 16.8 kB
build/edit-widgets/style-rtl.css 4.46 kB
build/edit-widgets/style.css 4.47 kB
build/editor/style-rtl.css 3.69 kB
build/editor/style.css 3.68 kB
build/element/index.min.js 4.93 kB
build/escape-html/index.min.js 548 B
build/experiments/index.min.js 882 B
build/format-library/index.min.js 7.2 kB
build/format-library/style-rtl.css 598 B
build/format-library/style.css 597 B
build/hooks/index.min.js 1.66 kB
build/html-entities/index.min.js 454 B
build/i18n/index.min.js 3.79 kB
build/is-shallow-equal/index.min.js 535 B
build/keyboard-shortcuts/index.min.js 1.79 kB
build/keycodes/index.min.js 1.88 kB
build/list-reusable-blocks/index.min.js 2.14 kB
build/list-reusable-blocks/style-rtl.css 865 B
build/list-reusable-blocks/style.css 865 B
build/media-utils/index.min.js 2.99 kB
build/notices/index.min.js 977 B
build/plugins/index.min.js 1.95 kB
build/preferences-persistence/index.min.js 2.23 kB
build/preferences/index.min.js 1.35 kB
build/primitives/index.min.js 960 B
build/priority-queue/index.min.js 1.59 kB
build/react-i18n/index.min.js 702 B
build/react-refresh-entry/index.min.js 8.44 kB
build/react-refresh-runtime/index.min.js 7.31 kB
build/redux-routine/index.min.js 2.75 kB
build/reusable-blocks/index.min.js 2.26 kB
build/reusable-blocks/style-rtl.css 283 B
build/reusable-blocks/style.css 283 B
build/rich-text/index.min.js 10.8 kB
build/server-side-render/index.min.js 2.09 kB
build/shortcode/index.min.js 1.52 kB
build/style-engine/index.min.js 1.53 kB
build/token-list/index.min.js 650 B
build/url/index.min.js 3.69 kB
build/vendors/inert-polyfill.min.js 2.48 kB
build/vendors/react-dom.min.js 41.8 kB
build/vendors/react.min.js 4.02 kB
build/viewport/index.min.js 1.09 kB
build/warning/index.min.js 280 B
build/widgets/index.min.js 7.31 kB
build/widgets/style-rtl.css 1.18 kB
build/widgets/style.css 1.18 kB
build/wordcount/index.min.js 1.06 kB

compressed-size-action

@ciampo ciampo force-pushed the refactor/number-control-value-type branch from d2366f8 to d211a80 Compare November 23, 2022 09:57
@ciampo ciampo changed the base branch from trunk to refactor/number-control-tests-stories-typescript November 23, 2022 10:03
@ciampo ciampo force-pushed the refactor/number-control-tests-stories-typescript branch from f644ed5 to f75fcd0 Compare November 24, 2022 08:46
@DAreRodz DAreRodz added [Package] Components /packages/components [Type] Code Quality Issues or PRs that relate to code quality and removed [Package] Components /packages/components labels Nov 24, 2022
@ciampo ciampo force-pushed the refactor/number-control-tests-stories-typescript branch 2 times, most recently from 98d8b1d to 6732efb Compare November 25, 2022 23:02
Base automatically changed from refactor/number-control-tests-stories-typescript to trunk November 25, 2022 23:31
@ciampo ciampo force-pushed the refactor/number-control-value-type branch from d211a80 to b815bd3 Compare November 26, 2022 11:26
Copy link
Contributor

@aaronrobertshaw aaronrobertshaw left a comment

Choose a reason for hiding this comment

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

I know there is still work in progress for this PR but I had a moment to give it a preliminary test.

It's looking good so far.

  • Other than the Uncontrolled storybook example, there weren't any typing errors
  • Unit tests pass (NumberControl, UnitControl, RangeControl, ColorPicker etc)
  • Controls all appear to be working ok in the Storybook and editor

is changing the type of onCHange args from number to string a breaking change, or a bug fix?

Tricky question to answer. Can it be both?

I'm biased given previous issues faced around this typing problem so I tend to lean towards it being a bug fix. It could easily be seen as a breaking change though for consumers that depended on the previous type.

packages/components/src/number-control/index.tsx Outdated Show resolved Hide resolved
@ciampo ciampo self-assigned this Dec 1, 2022
@ciampo ciampo force-pushed the refactor/number-control-value-type branch from b815bd3 to 0fa0e87 Compare December 2, 2022 10:52
@ciampo ciampo marked this pull request as ready for review December 2, 2022 14:08
@ciampo
Copy link
Contributor Author

ciampo commented Dec 2, 2022

PR is now ready for review — I've updated the description with more details :)

Comment on lines 103 to 145
describe( 'ensureString', () => {
it.each( [
[ '1', '1' ],
[ 'abc', 'abc' ],
[ 2e3, '2000' ],
[ 42, '42' ],
[ -14, '-14' ],
[ 0, '0' ],
[ '0', '0' ],
[ '', '' ],
[ NaN, 'NaN' ],
] )(
'should convert `%s` (unknown) to `%s` (string)',
( input, expectedOutput ) => {
expect( ensureString( input ) ).toBe( expectedOutput );
}
);
} );

describe( 'ensureNumber', () => {
it.each( [
[ '1', 1 ],
[ 'abc', NaN ],
[ 2e3, 2000 ],
[ 42, 42 ],
[ -14, -14 ],
[ 0, 0 ],
[ '0', 0 ],
[ '', NaN ],
[ 'NaN', NaN ],
] )(
'should convert `%s` (unknown) to `%s` (string)',
( input, expectedOutput ) => {
expect( ensureNumber( input ) ).toBe( expectedOutput );
}
);
} );
Copy link
Contributor Author

@ciampo ciampo Dec 2, 2022

Choose a reason for hiding this comment

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

The value prop in NumberControl will now be converted from string to a number, and back to a string.

Currently ensureString( ensureNumber( valueAsAString) ) ) !== valueAsAString for some edge cases: in particular, currently ensureNumber('') === NaN, but ensureString( NaN ) === 'NaN').

I was wondering if ensureString( NaN ) should return '' instead, and if that would have any ripercussions? (currently these utilities are only used by NumberControl, but I'd like for them to be used by other components dealing internally with numbers for better consistency)

Copy link
Member

Choose a reason for hiding this comment

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

It's simply a reality that a string cannot always be turned into a number. Can we have the type of ensureNumber reflect this?

function ensureNumber( value: string ): number | null

The type system will then guide users away from writing (incorrect) code that doesn't handle invalid strings.

Copy link
Contributor

Choose a reason for hiding this comment

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

Given null isn't a number would we also need to tweak the name of ensureNumber in that case?

Copy link
Contributor Author

@ciampo ciampo Jan 16, 2023

Choose a reason for hiding this comment

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

Given null isn't a number would we also need to tweak the name of ensureNumber in that case?

Yeah, I could rename that function to ensureValidNumber or ensureFiniteNumber. Any thoughts?

The type system will then guide users away from writing (incorrect) code that doesn't handle invalid strings.

I went ahead and changed ensureNumber and ensureString to return null when they are dealing with non-finite numbers (1da7361).

Not sure about the naming, but otherwise how does that feel like?

Copy link
Contributor Author

@ciampo ciampo Jan 16, 2023

Choose a reason for hiding this comment

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

I just realized that these changes break some unit tests, I'll have to look into that too.

Edit: force-pushed a new version of the same changes which should pass unit tests

@ciampo ciampo force-pushed the refactor/number-control-value-type branch from 5a3f164 to 1c0afa0 Compare January 16, 2023 23:19
@ciampo
Copy link
Contributor Author

ciampo commented Jan 16, 2023

Hey all! Back from some AFK, I've rebased and started to address some of the feedback. I will try to get to all feedback in the upcoming days

@ciampo
Copy link
Contributor Author

ciampo commented Jan 16, 2023

Not sure I completely understand this. Where in the code do we need to use a number to represent uncontrolled mode and empty but in controlled mode? We turn value into a string asap don't we?

@noisysocks Trying to interpret what 1.5 months ago Marco meant: since the convention is to

  • pass undefined as the value to tell the component to be in uncontrolled mode
  • pass '' as the value to tell the component "no value set" but in _controlled) mode

we need to be careful in the way we convert from string to number and viceversa especially around those falsey values (null, undefined, '', NaN..) especially when converting string to number (i.e parsing value prop) and back to string (i.e. onChange callback).

Hope that present Marco is making sense of past Marco 🔮

NumberControl's onChange is called with an unconstrained value when simply typing into the input. This seemed a bit odd giving we constrain the value before calling onChange when changing via up/down keys, dragging, and enter to confirm.

I'll to take a deeper look at this, thank you for flagging @aaronrobertshaw !

@ciampo ciampo force-pushed the refactor/number-control-value-type branch from 1c0afa0 to 38c1a2e Compare January 17, 2023 00:04
@ciampo ciampo force-pushed the refactor/number-control-value-type branch from 38c1a2e to 3ac25c9 Compare January 17, 2023 00:06
@noisysocks
Copy link
Member

noisysocks commented Jan 17, 2023

we need to be careful in the way we convert from string to number and viceversa especially around those falsey values (null, undefined, '', NaN..) especially when converting string to number (i.e parsing value prop) and back to string (i.e. onChange callback).

Right, yeah. Maybe a good exercise would be to complete this table:

Uncontrolled Controlled but empty
value : string undefined ''
value : number (to be deprecated) ? ?

@github-actions
Copy link

Flaky tests detected in bc58827.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/3937911316
📝 Reported issues:

@ciampo
Copy link
Contributor Author

ciampo commented Jan 18, 2023

Right, yeah. Maybe a good exercise would be to complete this table:

Inspired by this, I've started working on a separate PR to improve NumberControl's unit tests.
The plan is to cover these (and and other) edge cases, so that we can get a confidence boost before merging this PR.

export const ensureNumber = ( value ) => {
return typeof value === 'string' ? stringToNumber( value ) : value;
export const ensureFiniteNumber = ( value ) => {
if ( typeof value === 'number' ) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note to self: Make sure number is finite here too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Components /packages/components [Type] Code Quality Issues or PRs that relate to code quality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants