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

[WIP] Provide standardised tools for mapping Data to Link attributes #54791

Draft
wants to merge 12 commits into
base: trunk
Choose a base branch
from

Conversation

getdave
Copy link
Contributor

@getdave getdave commented Sep 25, 2023

What?

Provides a set of standardised functions to map stored data attributes into valid LinkControl value attributes.

Why?

There are numerous places in Core (and beyond) where some data must be mapped to the LinkControl's value object. This process must also happen in reverse when LinkControl onChange is called.

There are several implementations and each one has it's own quirks, is complex to reason about/change, and lacks automated test coverage.

In addition there are no extension points for how the data should be mapped/transformed, meaning that it is not possible to add additional settings/controls to the various Link UIs used throughtout the editor. Chief amongst these is the RichText implementation which 3rd party extenders regularly want to modify to add additional settings.

This PR seeks to resolve this by providing consumers with a standardised (largely declarative) API for describing how data objects should be mapped to LinkControl values.

It also paves the way for the possibility of making the "map" objects passed to these functions extensible. This would allow 3rd party extenders to add additional controls to the Link UI within the editor without the current process which requires re-registering the core/link format from scratch!

How?

Instead of imperitively coding how the link value should be decoded/encoded, instead we provide utility functions which act over standardised "map" objects which describe how the LinkControl's value fields map to the given data object.

By default the mapping works 1:1 based on string equivalence. So in the case below the url attribute of the LinkControl object is mapped to the href field of the data object. The data object might be block attributes or any other form of storage.

For more complex examples, consumers may define more complex transformations by way of toLink and toData which describe how the different values should be transformed.

Below is a simplified example of this:

// Define how LinkValue object should map to associate data values.
const mapping = {
	url: 'href',
	type: 'postType',
	id: 'id',
	opensInNewTab: {
		dataKey: 'linkTarget',
		toLink: ( dataAttr ) => toLinkDataAttr === '_blank', // `opensInNewTab should be `true` when `linkTarget` is `_blank`.
		toData: ( linkAttr ) => ( linkAttr ? '_blank' : undefined ), // if `opensInNewTab` is `true` then `linkTarget` should be set to `_blank`.
	},
}

// Now get the pre-bound transformation functions.
const { toLink, toData } = getLinkValueTransforms(mapping);

Now the values can be passed to either transformation function as required and the result will be the desired object ready to either:

  • pass directly to LinkControl as the value prop
  • persist to some storage mechanism (e.g. setAttributes())
// Build a `value` to pass to `LinkControl`.
const linkValue = toLink( blockData, mapping );

// Build object from a Link Value to be persisted (e.g. to block attributes or somewhere else).
const blockData = toData( linkValue, mapping );

Testing Instructions

Testing Instructions for Keyboard

Screenshots or screencast

@getdave getdave self-assigned this Sep 25, 2023
Comment on lines 1 to 38
export function buildLinkValueFromData( data, mapping ) {
const linkValue = {};
for ( const [ attributeName, valueGetter ] of Object.entries( mapping ) ) {
if ( typeof valueGetter === 'string' ) {
linkValue[ attributeName ] = data[ valueGetter ];
} else {
linkValue[ attributeName ] = valueGetter.toLink(
data[ valueGetter.dataKey ]
);
}
}
return linkValue;
}

export function buildDataFromLinkValue( linkValue, mapping ) {
const data = {};
for ( const [ attributeName, valueGetter ] of Object.entries( mapping ) ) {
if ( typeof valueGetter === 'string' ) {
data[ valueGetter ] = linkValue[ attributeName ];
} else {
data[ valueGetter.dataKey ] = valueGetter.toData(
linkValue[ attributeName ],
data[ valueGetter.dataKey ]
);
}
}
return data;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These two functions can likely be normalized into a single function. Or at least they can be well-named abstractions over that single utility function.

@github-actions
Copy link

github-actions bot commented Sep 25, 2023

Size Change: +263 B (0%)

Total Size: 1.65 MB

Filename Size Change
build/block-editor/index.min.js 218 kB +118 B (0%)
build/block-library/index.min.js 211 kB +145 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 964 B
build/annotations/index.min.js 2.71 kB
build/api-fetch/index.min.js 2.29 kB
build/autop/index.min.js 2.11 kB
build/blob/index.min.js 461 B
build/block-directory/index.min.js 7.07 kB
build/block-directory/style-rtl.css 1.04 kB
build/block-directory/style.css 1.04 kB
build/block-editor/content-rtl.css 4.28 kB
build/block-editor/content.css 4.27 kB
build/block-editor/default-editor-styles-rtl.css 403 B
build/block-editor/default-editor-styles.css 403 B
build/block-editor/style-rtl.css 15.6 kB
build/block-editor/style.css 15.6 kB
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 104 B
build/block-library/blocks/avatar/style.css 104 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 587 B
build/block-library/blocks/button/editor.css 587 B
build/block-library/blocks/button/style-rtl.css 633 B
build/block-library/blocks/button/style.css 632 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 113 B
build/block-library/blocks/categories/editor.css 112 B
build/block-library/blocks/categories/style-rtl.css 124 B
build/block-library/blocks/categories/style.css 124 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 421 B
build/block-library/blocks/columns/style.css 421 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 647 B
build/block-library/blocks/cover/editor.css 650 B
build/block-library/blocks/cover/style-rtl.css 1.7 kB
build/block-library/blocks/cover/style.css 1.69 kB
build/block-library/blocks/details/editor-rtl.css 65 B
build/block-library/blocks/details/editor.css 65 B
build/block-library/blocks/details/style-rtl.css 98 B
build/block-library/blocks/details/style.css 98 B
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 316 B
build/block-library/blocks/file/editor.css 316 B
build/block-library/blocks/file/style-rtl.css 311 B
build/block-library/blocks/file/style.css 312 B
build/block-library/blocks/file/view.min.js 321 B
build/block-library/blocks/footnotes/style-rtl.css 201 B
build/block-library/blocks/footnotes/style.css 199 B
build/block-library/blocks/form-input/editor-rtl.css 229 B
build/block-library/blocks/form-input/editor.css 228 B
build/block-library/blocks/form-input/style-rtl.css 343 B
build/block-library/blocks/form-input/style.css 343 B
build/block-library/blocks/form-submission-notification/editor-rtl.css 343 B
build/block-library/blocks/form-submission-notification/editor.css 342 B
build/block-library/blocks/form-submit-button/style-rtl.css 69 B
build/block-library/blocks/form-submit-button/style.css 69 B
build/block-library/blocks/form/view.min.js 452 B
build/block-library/blocks/freeform/editor-rtl.css 2.61 kB
build/block-library/blocks/freeform/editor.css 2.61 kB
build/block-library/blocks/gallery/editor-rtl.css 957 B
build/block-library/blocks/gallery/editor.css 962 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 189 B
build/block-library/blocks/heading/style.css 189 B
build/block-library/blocks/html/editor-rtl.css 340 B
build/block-library/blocks/html/editor.css 341 B
build/block-library/blocks/image/editor-rtl.css 834 B
build/block-library/blocks/image/editor.css 833 B
build/block-library/blocks/image/style-rtl.css 1.45 kB
build/block-library/blocks/image/style.css 1.44 kB
build/block-library/blocks/image/theme-rtl.css 137 B
build/block-library/blocks/image/theme.css 137 B
build/block-library/blocks/image/view.min.js 1.83 kB
build/block-library/blocks/latest-comments/style-rtl.css 357 B
build/block-library/blocks/latest-comments/style.css 357 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 505 B
build/block-library/blocks/media-text/style.css 503 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 671 B
build/block-library/blocks/navigation-link/editor.css 672 B
build/block-library/blocks/navigation-link/style-rtl.css 103 B
build/block-library/blocks/navigation-link/style.css 103 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.26 kB
build/block-library/blocks/navigation/editor.css 2.26 kB
build/block-library/blocks/navigation/style-rtl.css 2.26 kB
build/block-library/blocks/navigation/style.css 2.25 kB
build/block-library/blocks/navigation/view.min.js 1.01 kB
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 401 B
build/block-library/blocks/page-list/editor.css 401 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 235 B
build/block-library/blocks/paragraph/editor.css 235 B
build/block-library/blocks/paragraph/style-rtl.css 335 B
build/block-library/blocks/paragraph/style.css 335 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 508 B
build/block-library/blocks/post-comments-form/style.css 508 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 71 B
build/block-library/blocks/post-excerpt/editor.css 71 B
build/block-library/blocks/post-excerpt/style-rtl.css 141 B
build/block-library/blocks/post-excerpt/style.css 141 B
build/block-library/blocks/post-featured-image/editor-rtl.css 588 B
build/block-library/blocks/post-featured-image/editor.css 586 B
build/block-library/blocks/post-featured-image/style-rtl.css 345 B
build/block-library/blocks/post-featured-image/style.css 345 B
build/block-library/blocks/post-navigation-link/style-rtl.css 215 B
build/block-library/blocks/post-navigation-link/style.css 214 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 409 B
build/block-library/blocks/post-template/style.css 408 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-time-to-read/style-rtl.css 69 B
build/block-library/blocks/post-time-to-read/style.css 69 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 125 B
build/block-library/blocks/preformatted/style.css 125 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 335 B
build/block-library/blocks/pullquote/style.css 335 B
build/block-library/blocks/pullquote/theme-rtl.css 168 B
build/block-library/blocks/pullquote/theme.css 168 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 486 B
build/block-library/blocks/query/editor.css 486 B
build/block-library/blocks/query/style-rtl.css 375 B
build/block-library/blocks/query/style.css 372 B
build/block-library/blocks/query/view.min.js 609 B
build/block-library/blocks/quote/style-rtl.css 222 B
build/block-library/blocks/quote/style.css 222 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 149 B
build/block-library/blocks/rss/editor.css 149 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 184 B
build/block-library/blocks/search/editor.css 184 B
build/block-library/blocks/search/style-rtl.css 613 B
build/block-library/blocks/search/style.css 613 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/search/view.min.js 471 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 329 B
build/block-library/blocks/shortcode/editor.css 329 B
build/block-library/blocks/site-logo/editor-rtl.css 760 B
build/block-library/blocks/site-logo/editor.css 760 B
build/block-library/blocks/site-logo/style-rtl.css 204 B
build/block-library/blocks/site-logo/style.css 204 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 682 B
build/block-library/blocks/social-links/editor.css 681 B
build/block-library/blocks/social-links/style-rtl.css 1.45 kB
build/block-library/blocks/social-links/style.css 1.45 kB
build/block-library/blocks/spacer/editor-rtl.css 359 B
build/block-library/blocks/spacer/editor.css 359 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 432 B
build/block-library/blocks/table/editor.css 432 B
build/block-library/blocks/table/style-rtl.css 646 B
build/block-library/blocks/table/style.css 645 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 403 B
build/block-library/blocks/template-part/editor.css 403 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/term-description/style-rtl.css 111 B
build/block-library/blocks/term-description/style.css 111 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 552 B
build/block-library/blocks/video/editor.css 555 B
build/block-library/blocks/video/style-rtl.css 191 B
build/block-library/blocks/video/style.css 191 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 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.11 kB
build/block-library/common.css 1.11 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 12.4 kB
build/block-library/editor.css 12.4 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/style-rtl.css 14.3 kB
build/block-library/style.css 14.3 kB
build/block-library/theme-rtl.css 700 B
build/block-library/theme.css 705 B
build/block-serialization-default-parser/index.min.js 1.13 kB
build/block-serialization-spec-parser/index.min.js 2.87 kB
build/blocks/index.min.js 51.5 kB
build/commands/index.min.js 15.5 kB
build/commands/style-rtl.css 947 B
build/commands/style.css 942 B
build/components/index.min.js 248 kB
build/components/style-rtl.css 11.9 kB
build/components/style.css 11.9 kB
build/compose/index.min.js 12.7 kB
build/core-commands/index.min.js 2.72 kB
build/core-data/index.min.js 70.6 kB
build/customize-widgets/index.min.js 12 kB
build/customize-widgets/style-rtl.css 1.51 kB
build/customize-widgets/style.css 1.5 kB
build/data-controls/index.min.js 651 B
build/data/index.min.js 8.78 kB
build/date/index.min.js 17.9 kB
build/deprecated/index.min.js 462 B
build/dom-ready/index.min.js 336 B
build/dom/index.min.js 4.68 kB
build/edit-post/classic-rtl.css 571 B
build/edit-post/classic.css 571 B
build/edit-post/index.min.js 35.6 kB
build/edit-post/style-rtl.css 7.89 kB
build/edit-post/style.css 7.88 kB
build/edit-site/index.min.js 203 kB
build/edit-site/style-rtl.css 14.1 kB
build/edit-site/style.css 14.1 kB
build/edit-widgets/index.min.js 17 kB
build/edit-widgets/style-rtl.css 4.84 kB
build/edit-widgets/style.css 4.84 kB
build/editor/index.min.js 45.9 kB
build/editor/style-rtl.css 3.58 kB
build/editor/style.css 3.58 kB
build/element/index.min.js 4.87 kB
build/escape-html/index.min.js 548 B
build/format-library/index.min.js 7.79 kB
build/format-library/style-rtl.css 577 B
build/format-library/style.css 577 B
build/hooks/index.min.js 1.57 kB
build/html-entities/index.min.js 454 B
build/i18n/index.min.js 3.61 kB
build/interactivity/index.min.js 11.4 kB
build/is-shallow-equal/index.min.js 535 B
build/keyboard-shortcuts/index.min.js 1.76 kB
build/keycodes/index.min.js 1.9 kB
build/list-reusable-blocks/index.min.js 2.21 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.92 kB
build/notices/index.min.js 964 B
build/nux/index.min.js 2.01 kB
build/nux/style-rtl.css 775 B
build/nux/style.css 771 B
build/patterns/index.min.js 3.57 kB
build/patterns/style-rtl.css 325 B
build/patterns/style.css 325 B
build/plugins/index.min.js 1.81 kB
build/preferences-persistence/index.min.js 1.85 kB
build/preferences/index.min.js 1.26 kB
build/primitives/index.min.js 994 B
build/priority-queue/index.min.js 1.52 kB
build/private-apis/index.min.js 972 B
build/react-i18n/index.min.js 631 B
build/react-refresh-entry/index.min.js 9.46 kB
build/react-refresh-runtime/index.min.js 6.78 kB
build/redux-routine/index.min.js 2.71 kB
build/reusable-blocks/index.min.js 2.73 kB
build/reusable-blocks/style-rtl.css 265 B
build/reusable-blocks/style.css 265 B
build/rich-text/index.min.js 10.2 kB
build/router/index.min.js 1.79 kB
build/server-side-render/index.min.js 1.96 kB
build/shortcode/index.min.js 1.4 kB
build/style-engine/index.min.js 1.98 kB
build/token-list/index.min.js 587 B
build/url/index.min.js 3.84 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 967 B
build/warning/index.min.js 259 B
build/widgets/index.min.js 7.18 kB
build/widgets/style-rtl.css 1.18 kB
build/widgets/style.css 1.18 kB
build/wordcount/index.min.js 1.03 kB

compressed-size-action

@getdave getdave added the [Type] Experimental Experimental feature or API. label Sep 25, 2023
@getdave
Copy link
Contributor Author

getdave commented Sep 25, 2023

Next step here is to try and utilise the new mapping functions inside:

  • Nav link
  • Button block
  • RichText

Need PoCs and also to stress test the utils. All new scenarios encountered should be encoded into the tests.

@getdave
Copy link
Contributor Author

getdave commented Sep 25, 2023

Pinging @bangank36 who did some work around building link attributes in #54110.

I wonder if you feel these functions could replace getUpdatedLinkAttributes in that PR? Anything I'm missing in the API?

@bangank36
Copy link
Contributor

bangank36 commented Sep 26, 2023

@getdave Thank you for the ping!
I fetched the PR and make some early tests, here are my thoughts

By default the mapping works 1:1 based on string equivalence

I like this behavior, I used to be confused with the conversion of linkTarget <-> target in Button Link in #54110, this kind of mapping can help identify the problem faster

I wonder if you feel these functions could replace getUpdatedLinkAttributes in that PR

With the current implementation, I think these functions can only work well with 1:1 mapping, while getUpdatedLinkAttributes function is performing transformations that go beyond simple attribute mapping, for example: opensInNewTab will not only modify linkTarget but also the linkRel.
In such cases, we may need to retain getUpdatedLinkAttributes or refactor it accordingly.

@getdave
Copy link
Contributor Author

getdave commented Sep 26, 2023

opensInNewTab will not only modify linkTarget but also the linkRel.

This is a great example. Thank you. I'll see if I can refactor to allow for this.

@getdave getdave force-pushed the try/standardise-link-to-data-mapping branch from 297f0f7 to 4a34767 Compare September 29, 2023 11:00
updatedRel = updatedRel?.replace( relRegex, '' ).trim();
}

// Handle setting rel based on opensInNewTab setting.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do we think about handling another attribute in this handler? If we don't do it like this we will need some means within toData to trigger mutations in other parts of the data tree.

Whilst the approach here isn't one I love, it feels less complex to the consumer than any alternative I can conceive of right now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also this might cause unexpected conditions because what if opensInNewTab were processed after nofollow? That might mean that opensInNewTab value might still represent the old value as it has yet to be processed.

We need a test for such a scenario to make it concrete and then we can consider how best to resolve it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Possibly a "dependencies" idea, whereby properties can declare that they have dependencies on other properties. Then the final value for the property is only calculated once the other property has been processed.

Copy link
Contributor

@bangank36 bangank36 Oct 7, 2023

Choose a reason for hiding this comment

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

@getdave I have thought about this, can we somehow define util methods for the mapping object

  1. Function that returns mapping object
const mapping = ( () => {
    const utils = {
        method1: ()=>{},
        ...
    }
    return {
       'url': 'href'
       'nofollow': ()=>{ utils.method1(); } 
   }
} )();
  1. Object with utilities methods
const mapping = {
    'url': 'href',
    'utils': {
        method1: ()=>{},
        ...
    }
}

With this approach, we may need to exclude the prop name from the link-value-transforms methods, since it does not have standard toLink and toData methods like normal props

Copy link
Contributor

Choose a reason for hiding this comment

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

I made a PR for demo this #55143

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've also been thinking about ths concept of utility functions. I think on balance it might be easier to simply allow consumers to provide their own utilities as needed.

For example much of the transforms could be handled with a functional approach using something like compose to compose a set of transformations.

I think the most common one will be "if this value is a string then concat to an existing string if it exists or otherwise just use the string as the new value". Something like Ramda Concat but that can handle undefined would be what we need.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh I've just realised the concept in your PR 👍

So we could export an additional helper from link-value-transforms called mapGenerator where we could expose utils for mapping.

If I've understood this would accept a mapping object and provide callbacks to the map to allow the consumer to perform typical data operations.

This would also be completely optional.

I think it's clever. I think we should

  • implement a basic version of this (my) PR
  • come back to your idea of mapping generators in a follow up.

I really like the idea but I think it may overcomplicate this initial PR. But I would definitely like to explore it.

What do you think?

Copy link
Contributor

@bangank36 bangank36 Oct 9, 2023

Choose a reason for hiding this comment

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

This would also be completely optional.

Yes, it is
The PR #55143 should be a commit to modify the ButtonEdit mapping, but because of the lack of write privilege, I used it as a separate proposal with its own test for better explanation

I think on balance it might be easier to simply allow consumers to provide their own utilities as needed.

The same explain as above, the PR was meant for ButtonEdit (as consumer) with it own utils

Comment on lines 30 to 35
if ( value && currentVal ) {
return `${ currentVal } nofollow`;
} else if ( value ) {
return 'nofollow';
}
Copy link
Contributor Author

@getdave getdave Sep 29, 2023

Choose a reason for hiding this comment

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

This type of "append to existing" operation feels like it could be fairly common. Should we look to provide some kind of utility to make this simpler and avoid boilerplate?

Comment on lines 162 to 214
const linkValue = useMemo(
() => ( { url, opensInNewTab, nofollow } ),
[ url, opensInNewTab, nofollow ]
const linkValue = buildLinkValueFromData(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

⚠️ Do not approve or merge this PR without reinstating the memoization here.

Copy link
Contributor Author

@getdave getdave Sep 29, 2023

Choose a reason for hiding this comment

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

I think we just need to wrap buildLinkValueFromData in a useCallback and then use it inside of the useMemo.

@getdave getdave added the [Feature] Link Editing Link components (LinkControl, URLInput) and integrations (RichText link formatting) label Sep 29, 2023
@github-actions
Copy link

github-actions bot commented Sep 29, 2023

Flaky tests detected in 2eb7259.
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/6430348972
📝 Reported issues:

@getdave
Copy link
Contributor Author

getdave commented Sep 29, 2023

The next objective here is to modify the Nav Link block to utilise the same utility functions and allow us to reduce the complexity inside of updateAttributes.

@getdave getdave force-pushed the try/standardise-link-to-data-mapping branch from 9ac0b0a to 63a5b19 Compare October 6, 2023 10:12
} );

describe( 'building data from a link value', () => {
it( 'build a valid data object from supplied link value mapping', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We'll need to break this into discreet tests asserting on the behaviour. We should do this before things get overly complex 😓

// ...( kind && { kind } ),
// ...( type && type !== 'URL' && { type } ),

const { toLink, toData } = getLinkValueTransforms( {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Very much a WIP. Does not work...yet

Comment on lines +209 to +210
{ url: newUrl = '' },
{ label: originalLabel = '' }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Whilst terse, this does require the reader to understand the API of the toData reducer.

Instead let's define these as well named arguments (e.g. fullLinkValue, currentDataValue) and then destructure from them.

Comment on lines +250 to +252
url,
label,
opensInNewTab,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We should probably have this as a well renamed const and define it wherever we access these values further up the component.

postType: 'post',
id: 123,
linkTarget: '_blank',
linkRel: 'nofollow sponsored',
Copy link
Contributor

Choose a reason for hiding this comment

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

@getdave I think the expected linkRel should be noopener noreferer nofollow sponsored because the linkValue is

opensInNewTab: true,
noFollow: true,
sponsored: true,

This test case is passed with incorrect value...

Copy link
Contributor

Choose a reason for hiding this comment

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

The failed test is found when running unit test against link-value-transforms-with-utils.js

Copy link
Contributor

Choose a reason for hiding this comment

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

@getdave but I think this is expected with the current mapping object in the test file, because the default mapping is 1:1, therefore nofollow and sponsored linked with linkRel while opensInNewTab is not, so the expected linkRel is correct 'nofollow sponsored'

Copy link
Contributor

@bangank36 bangank36 left a comment

Choose a reason for hiding this comment

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

It looks like I don't have privilege to push commit to this PR, therefore I opened new PR to propose my approach #55143 on the mapping object
Instead of using string concatenation, I used Set to ensure unique values and simplify management

@getdave
Copy link
Contributor Author

getdave commented Oct 9, 2023

It looks like I don't have privilege to push commit to this PR

How many PRs have you merged into the Gutenberg repo? If it's 3 or more I can request that you be given repo rights.

Instead of using string concatenation, I used Set to ensure unique values and simplify management

I took a quick look at the PR - thank you. It seems like the change is to the test files right? If so I'm not sure I agree we should have this level of abstraction within a test.

In general, I'd prefer to aim for everything to be concrete even if that means lots of duplication and manual string concat.

Why? Because by keeping tests simple we avoid writing test code that itself needs testing therefore reducing the possibility of false positives in tests due to bugs in test code. Also it is easier for those unfamiliar with the code under test to perceive and work with the tests.

You have spotted some errors above which we should fix. Thank you 🙇

@bangank36
Copy link
Contributor

bangank36 commented Oct 9, 2023

How many PRs have you merged into the Gutenberg repo? If it's 3 or more I can request that you be given repo rights.

There are 9 thus far

It seems like the change is to the test files right?

No, it's the suggestion for changing the implementation of the mapping for ButtonEditor, as explained here #54791 (comment)

You have spotted some errors above which we should fix. Thank you 🙇

I have some more thoughts on this... #54791 (comment), I think the test is correct

@getdave
Copy link
Contributor Author

getdave commented Oct 9, 2023

How many PRs have you merged into the Gutenberg repo? If it's 3 or more I can request that you be given repo rights.

There are 9 thus far

I made a request on the WP Slack instance (sign up required).

@bangank36
Copy link
Contributor

I made a request on the WP Slack instance (sign up required).

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Link Editing Link components (LinkControl, URLInput) and integrations (RichText link formatting) [Type] Experimental Experimental feature or API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants