Skip to content
32 changes: 26 additions & 6 deletions packages/block-library/src/html/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ import {
PlainText,
useBlockProps,
} from '@wordpress/block-editor';
import { ToolbarButton, Disabled, ToolbarGroup } from '@wordpress/components';
import {
ToolbarButton,
Disabled,
ToolbarGroup,
VisuallyHidden,
} from '@wordpress/components';
import { useInstanceId } from '@wordpress/compose';

/**
* Internal dependencies
Expand All @@ -19,6 +25,8 @@ export default function HTMLEdit( { attributes, setAttributes, isSelected } ) {
const [ isPreview, setIsPreview ] = useState();
const isDisabled = useContext( Disabled.Context );

const instanceId = useInstanceId( HTMLEdit, 'html-edit-desc' );

function switchToPreview() {
setIsPreview( true );
}
Expand All @@ -27,8 +35,13 @@ export default function HTMLEdit( { attributes, setAttributes, isSelected } ) {
setIsPreview( false );
}

const blockProps = useBlockProps( {
className: 'block-library-html__edit',
'aria-describedby': isPreview ? instanceId : undefined,
} );

return (
<div { ...useBlockProps( { className: 'block-library-html__edit' } ) }>
<div { ...blockProps }>
<BlockControls>
<ToolbarGroup>
<ToolbarButton
Expand All @@ -48,10 +61,17 @@ export default function HTMLEdit( { attributes, setAttributes, isSelected } ) {
</ToolbarGroup>
</BlockControls>
{ isPreview || isDisabled ? (
<Preview
content={ attributes.content }
isSelected={ isSelected }
/>
<>
<Preview
content={ attributes.content }
isSelected={ isSelected }
/>
<VisuallyHidden id={ instanceId }>
{ __(
'HTML preview is not yet fully accessible. Please switch screen reader to virtualized mode to navigate the below iFrame.'
) }
</VisuallyHidden>
</>
) : (
<PlainText
value={ attributes.content }
Expand Down
8 changes: 7 additions & 1 deletion packages/block-library/src/html/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '@wordpress/block-editor';
import { SandBox } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

// Default styles used to unset some of the styles
// that might be inherited from the editor style.
Expand All @@ -32,7 +33,12 @@ export default function HTMLEditPreview( { content, isSelected } ) {

return (
<>
<SandBox html={ content } styles={ styles } />
<SandBox
html={ content }
styles={ styles }
title={ __( 'Custom HTML Preview' ) }
tabIndex={ -1 }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added this because focus should not enter iFrame unless user wants it to. There is no way to escape the iFrame currently since writing flow is unaware of 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.

Is there an easy fix for this? I guess not since the secondary iFrame is a different browser context.

/>
{ /*
An overlay is added when the block is not selected in order to register click events.
Some browsers do not bubble up the clicks from the sandboxed iframe, which makes it
Expand Down
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- `DuotonePicker/ColorListPicker`: Adds appropriate labels to 'Duotone Filter' color pickers ([#54468](https://github.com/WordPress/gutenberg/pull/54468)).
- `SearchControl`: support new `40px` and `32px` sizes ([#54548](https://github.com/WordPress/gutenberg/pull/54548)).
- `FormTokenField`: Add `tokenizeOnBlur` prop to add any incompleteTokenValue as a new token when field loses focus ([#54445](https://github.com/WordPress/gutenberg/pull/54445)).
- `Sandbox`: Add `tabIndex` prop ([#54408](https://github.com/WordPress/gutenberg/pull/54408)).

### Bug Fix

Expand Down
8 changes: 7 additions & 1 deletion packages/components/src/sandbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,10 @@ The `<title>` of the iframe document.
The CSS class name to apply to the `<html>` and `<body>` elements of the iframe.

- Required: No
- Default: ""
- Default: ""

### `tabIndex`: `HTMLElement[ 'tabIndex' ]`

The `tabindex` the iframe should receive.

- Required: No
2 changes: 2 additions & 0 deletions packages/components/src/sandbox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ function SandBox( {
styles = [],
scripts = [],
onFocus,
tabIndex,
}: SandBoxProps ) {
const ref = useRef< HTMLIFrameElement >();
const [ width, setWidth ] = useState( 0 );
Expand Down Expand Up @@ -282,6 +283,7 @@ function SandBox( {
<iframe
ref={ useMergeRefs( [ ref, useFocusableIframe() ] ) }
title={ title }
tabIndex={ tabIndex }
className="components-sandbox"
sandbox="allow-scripts allow-same-origin allow-presentation"
onFocus={ onFocus }
Expand Down
6 changes: 6 additions & 0 deletions packages/components/src/sandbox/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,10 @@ export type SandBoxProps = {
* The `onFocus` callback for the iframe.
*/
onFocus?: React.DOMAttributes< HTMLIFrameElement >[ 'onFocus' ];
/**
* The `tabindex` the iframe should receive.
*
* @default 0
*/
tabIndex?: HTMLElement[ 'tabIndex' ];
};