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

Feature: Added InnerBlock Support for Download Button & Add Support for download attribute #68510

Open
wants to merge 3 commits into
base: trunk
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/reference-guides/core-blocks.md
Original file line number Diff line number Diff line change
@@ -52,7 +52,7 @@ Prompt visitors to take action with a button-style link. ([Source](https://githu
- **Category:** design
- **Parent:** core/buttons
- **Supports:** anchor, color (background, gradients, text), interactivity (clientNavigation), shadow (), spacing (padding), splitting, typography (fontSize, lineHeight), ~~alignWide~~, ~~align~~, ~~reusable~~
- **Attributes:** backgroundColor, gradient, linkTarget, placeholder, rel, tagName, text, textAlign, textColor, title, type, url, width
- **Attributes:** backgroundColor, download, gradient, linkTarget, placeholder, rel, tagName, text, textAlign, textColor, title, type, url, width

## Buttons

@@ -273,7 +273,7 @@ Add a link to a downloadable file. ([Source](https://github.com/WordPress/gutenb
- **Name:** core/file
- **Category:** media
- **Supports:** align, anchor, color (background, gradients, link, ~~text~~), interactivity, spacing (margin, padding)
- **Attributes:** blob, displayPreview, downloadButtonText, fileId, fileName, href, id, previewHeight, showDownloadButton, textLinkHref, textLinkTarget
- **Attributes:** blob, displayPreview, fileId, fileName, href, id, previewHeight, showDownloadButton, textLinkHref, textLinkTarget

## Footnotes

7 changes: 7 additions & 0 deletions packages/block-library/src/button/block.json
Original file line number Diff line number Diff line change
@@ -55,6 +55,13 @@
"attribute": "rel",
"role": "content"
},
"download": {
"type": "boolean",
"source": "attribute",
"selector": "a",
"attribute": "download",
"role": "content"
},
"placeholder": {
"type": "string"
},
6 changes: 6 additions & 0 deletions packages/block-library/src/button/edit.js
Original file line number Diff line number Diff line change
@@ -59,6 +59,10 @@ const LINK_SETTINGS = [
id: 'nofollow',
title: __( 'Mark as nofollow' ),
},
{
id: 'download',
title: __( 'Download file' ),
},
];

function useEnter( props ) {
@@ -380,13 +384,15 @@ function ButtonEdit( props ) {
url: newURL,
opensInNewTab: newOpensInNewTab,
nofollow: newNofollow,
download: newDownload,
} ) =>
setAttributes(
getUpdatedLinkAttributes( {
rel,
url: newURL,
opensInNewTab: newOpensInNewTab,
nofollow: newNofollow,
download: newDownload,
} )
)
}
Original file line number Diff line number Diff line change
@@ -16,12 +16,14 @@ import { prependHTTP } from '@wordpress/url';
* @param {string} attributes.url The current link url.
* @param {boolean} attributes.opensInNewTab Whether the link should open in a new window.
* @param {boolean} attributes.nofollow Whether the link should be marked as nofollow.
* @param {boolean} attributes.download Whether the link should allow download.
*/
export function getUpdatedLinkAttributes( {
rel = '',
url = '',
opensInNewTab,
nofollow,
download = false,
} ) {
let newLinkTarget;
// Since `rel` is editable attribute, we need to check for existing values and proceed accordingly.
@@ -46,9 +48,33 @@ export function getUpdatedLinkAttributes( {
updatedRel = updatedRel?.replace( relRegex, '' ).trim();
}

const allowDownload = url && isSameOrigin( url ) ? download : undefined;

return {
url: prependHTTP( url ),
linkTarget: newLinkTarget,
rel: updatedRel || undefined,
download: allowDownload,
};
}

/**
* Checks if the URL is same origin.
* Allow relative URLs.
*
* @param {string} urlString The URL to check.
* @return {boolean} Whether the URL is same origin.
*/
function isSameOrigin( urlString ) {
// Allow relative URLs
if ( urlString.startsWith( '/' ) ) {
return true;
}

try {
const url = new URL( urlString, window.location.origin );
return url.origin === window.location.origin;
} catch {
return false;
}
}
2 changes: 2 additions & 0 deletions packages/block-library/src/button/save.js
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ export default function save( { attributes, className } ) {
title,
url,
width,
download,
} = attributes;

const TagName = tagName || 'a';
@@ -83,6 +84,7 @@ export default function save( { attributes, className } ) {
value={ text }
target={ isButtonTag ? null : linkTarget }
rel={ isButtonTag ? null : rel }
download={ isButtonTag ? null : download }
/>
</div>
);
6 changes: 0 additions & 6 deletions packages/block-library/src/file/block.json
Original file line number Diff line number Diff line change
@@ -48,12 +48,6 @@
"type": "boolean",
"default": true
},
"downloadButtonText": {
"type": "rich-text",
"source": "rich-text",
"selector": "a[download]",
"role": "content"
},
"displayPreview": {
"type": "boolean"
},
73 changes: 32 additions & 41 deletions packages/block-library/src/file/edit.js
Original file line number Diff line number Diff line change
@@ -21,11 +21,11 @@ import {
RichText,
useBlockProps,
store as blockEditorStore,
__experimentalGetElementClassName,
InnerBlocks,
} from '@wordpress/block-editor';
import { useEffect, useState } from '@wordpress/element';
import { useState } from '@wordpress/element';
import { useCopyToClipboard } from '@wordpress/compose';
import { __, _x } from '@wordpress/i18n';
import { __ } from '@wordpress/i18n';
import { file as icon } from '@wordpress/icons';
import { store as coreStore } from '@wordpress/core-data';
import { store as noticesStore } from '@wordpress/notices';
@@ -69,7 +69,6 @@ function FileEdit( { attributes, isSelected, setAttributes, clientId } ) {
textLinkHref,
textLinkTarget,
showDownloadButton,
downloadButtonText,
displayPreview,
previewHeight,
} = attributes;
@@ -85,27 +84,14 @@ function FileEdit( { attributes, isSelected, setAttributes, clientId } ) {
);

const { createErrorNotice } = useDispatch( noticesStore );
const { toggleSelection, __unstableMarkNextChangeAsNotPersistent } =
useDispatch( blockEditorStore );
const { toggleSelection } = useDispatch( blockEditorStore );

useUploadMediaFromBlobURL( {
url: temporaryURL,
onChange: onSelectFile,
onError: onUploadError,
} );

// Note: Handle setting a default value for `downloadButtonText` via HTML API
// when it supports replacing text content for HTML tags.
useEffect( () => {
if ( RichText.isEmpty( downloadButtonText ) ) {
__unstableMarkNextChangeAsNotPersistent();
setAttributes( {
downloadButtonText: _x( 'Download', 'button label' ),
} );
}
// This effect should only run on mount.
}, [] );

function onSelectFile( newMedia ) {
if ( ! newMedia || ! newMedia.url ) {
// Reset attributes.
@@ -302,29 +288,34 @@ function FileEdit( { attributes, isSelected, setAttributes, clientId } ) {
href={ textLinkHref }
/>
{ showDownloadButton && (
<div className="wp-block-file__button-richtext-wrapper">
{ /* Using RichText here instead of PlainText so that it can be styled like a button. */ }
<RichText
identifier="downloadButtonText"
tagName="div" // Must be block-level or else cursor disappears.
aria-label={ __( 'Download button text' ) }
className={ clsx(
'wp-block-file__button',
__experimentalGetElementClassName(
'button'
)
) }
value={ downloadButtonText }
withoutInteractiveFormatting
placeholder={ __( 'Add text…' ) }
onChange={ ( text ) =>
setAttributes( {
downloadButtonText:
removeAnchorTag( text ),
} )
}
/>
</div>
<InnerBlocks
Copy link
Member

Choose a reason for hiding this comment

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

In almost all cases, you want to use useInnerBlocksProps rather than <InnerBlocks /> these days because that allows you to match the markup between the editor and the frontend (you don't get additional div elements injected in the editor)

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 see, thanks for pointing that out. I'll look into updating it to use useInnerBlocksProps!

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 have updated the code to reflect the changes with useInnerBlocksProps. Thanks

allowedBlocks={ [ 'core/buttons' ] }
template={ [
[
'core/buttons',
{},
[
[
'core/button',
{
text: __( 'Download' ),
lock: {
remove: true,
move: true,
},
url: href || temporaryURL,
download: true,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

download attribute is missing, should we have it added to button component or should we go with a work around here?

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 went ahead with implementing the download attribute on button block for now, I think there is a need for discussion if alternative exists but I feel implementing download attribute is better way to handle this.

ariaLabel: __(
'Download button text'
),
},
],
],
],
] }
templateLock="all"
renderAppender={ false }
/>
) }
</div>
</div>
15 changes: 9 additions & 6 deletions packages/block-library/src/file/editor.scss
Original file line number Diff line number Diff line change
@@ -33,7 +33,15 @@
}

.wp-block-file__content-wrapper {
flex-grow: 1;
display: flex;
align-items: center;
gap: 1em;
flex-wrap: wrap;
width: 100%;

.block-editor-inner-blocks {
flex: 1;
}
}

a {
@@ -43,9 +51,4 @@
display: inline-block;
}
}

.wp-block-file__button-richtext-wrapper {
display: inline-block;
margin-left: 0.75em;
}
}
26 changes: 4 additions & 22 deletions packages/block-library/src/file/save.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
/**
* External dependencies
*/
import clsx from 'clsx';

/**
* WordPress dependencies
*/
import {
RichText,
useBlockProps,
__experimentalGetElementClassName,
} from '@wordpress/block-editor';
import { RichText, useBlockProps, InnerBlocks } from '@wordpress/block-editor';

export default function save( { attributes } ) {
const {
@@ -20,7 +11,6 @@ export default function save( { attributes } ) {
textLinkHref,
textLinkTarget,
showDownloadButton,
downloadButtonText,
displayPreview,
previewHeight,
} = attributes;
@@ -67,17 +57,9 @@ export default function save( { attributes } ) {
</a>
) }
{ showDownloadButton && (
<a
href={ href }
className={ clsx(
'wp-block-file__button',
__experimentalGetElementClassName( 'button' )
) }
download
aria-describedby={ describedById }
>
<RichText.Content value={ downloadButtonText } />
</a>
<div className="wp-block-file__button_wrapper">
<InnerBlocks.Content />
</div>
) }
</div>
)
42 changes: 25 additions & 17 deletions packages/block-library/src/file/style.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
.wp-block-file {
// This block has customizable padding, border-box makes that more predictable.
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 1em;

&:not(.wp-element-button) {
font-size: 0.8em;
@@ -18,6 +22,10 @@
* + .wp-block-file__button {
margin-left: 0.75em;
}

.wp-block-file__button_wrapper {
flex: 1;
}
}

// Lowest specificity to avoid overriding layout styles.
@@ -30,20 +38,20 @@
}

//This needs a low specificity so it won't override the rules from the button element if defined in theme.json.
:where(.wp-block-file__button) {
border-radius: 2em;
padding: 0.5em 1em;
display: inline-block;

&:is(a) {
&:hover,
&:visited,
&:focus,
&:active {
box-shadow: none;
color: $white;
opacity: 0.85;
text-decoration: none;
}
}
}
// :where(.wp-block-file__button) {
// border-radius: 2em;
// padding: 0.5em 1em;
// display: inline-block;

// &:is(a) {
// &:hover,
// &:visited,
// &:focus,
// &:active {
// box-shadow: none;
// color: $white;
// opacity: 0.85;
// text-decoration: none;
// }
// }
// }
Loading