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

Improve accessibility of the video track editor #66832

Merged
merged 7 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 9 additions & 0 deletions packages/block-library/src/video/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
max-width: 240px;
}

.block-library-video-tracks-editor__tracks-informative-message-title,
.block-library-video-tracks-editor__single-track-editor-edit-track-label {
margin-top: $grid-unit-05;
color: $gray-700;
Expand All @@ -56,3 +57,11 @@
padding: 0;
}
}

.block-library-video-tracks-editor__tracks-informative-message {
padding: $grid-unit-10;

&-description {
margin-bottom: 0;
}
}
296 changes: 157 additions & 139 deletions packages/block-library/src/video/tracks-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
} from '@wordpress/block-editor';
import { upload, media } from '@wordpress/icons';
import { useSelect } from '@wordpress/data';
import { useState } from '@wordpress/element';
import { useState, useRef, useEffect } from '@wordpress/element';
import { getFilename } from '@wordpress/url';

const ALLOWED_TYPES = [ 'text/vtt' ];
Expand All @@ -40,39 +40,29 @@ const KIND_OPTIONS = [
];

function TrackList( { tracks, onEditPress } ) {
let content;
if ( tracks.length === 0 ) {
content = (
<p className="block-library-video-tracks-editor__tracks-informative-message">
{ __(
'Tracks can be subtitles, captions, chapters, or descriptions. They help make your content more accessible to a wider range of users.'
) }
</p>
);
} else {
content = tracks.map( ( track, index ) => {
return (
<HStack
key={ index }
className="block-library-video-tracks-editor__track-list-track"
const content = tracks.map( ( track, index ) => {
return (
<HStack
key={ index }
className="block-library-video-tracks-editor__track-list-track"
>
<span>{ track.label }</span>
<Button
__next40pxDefaultSize
variant="tertiary"
onClick={ () => onEditPress( index ) }
aria-label={ sprintf(
/* translators: %s: Label of the video text track e.g: "French subtitles". */
_x( 'Edit %s', 'text tracks' ),
track.label
) }
>
<span>{ track.label } </span>
<Button
__next40pxDefaultSize
variant="tertiary"
onClick={ () => onEditPress( index ) }
aria-label={ sprintf(
/* translators: %s: Label of the video text track e.g: "French subtitles" */
_x( 'Edit %s', 'text tracks' ),
track.label
) }
>
{ __( 'Edit' ) }
</Button>
</HStack>
);
} );
}
{ __( 'Edit' ) }
</Button>
</HStack>
);
} );

return (
<MenuGroup
label={ __( 'Text tracks' ) }
Expand All @@ -87,105 +77,100 @@ function SingleTrackEditor( { track, onChange, onClose, onRemove } ) {
const { src = '', label = '', srcLang = '', kind = DEFAULT_KIND } = track;
const fileName = src.startsWith( 'blob:' ) ? '' : getFilename( src ) || '';
return (
<NavigableMenu>
<VStack
className="block-library-video-tracks-editor__single-track-editor"
spacing="4"
>
<span className="block-library-video-tracks-editor__single-track-editor-edit-track-label">
{ __( 'Edit track' ) }
</span>
<span>
{ __( 'File' ) }: <b>{ fileName }</b>
</span>
<Grid columns={ 2 } gap={ 4 }>
<TextControl
__next40pxDefaultSize
__nextHasNoMarginBottom
/* eslint-disable jsx-a11y/no-autofocus */
autoFocus
/* eslint-enable jsx-a11y/no-autofocus */
onChange={ ( newLabel ) =>
onChange( {
...track,
label: newLabel,
} )
}
label={ __( 'Label' ) }
value={ label }
help={ __( 'Title of track' ) }
/>
<TextControl
<VStack
className="block-library-video-tracks-editor__single-track-editor"
spacing="4"
>
<span className="block-library-video-tracks-editor__single-track-editor-edit-track-label">
{ __( 'Edit track' ) }
</span>
<span>
{ __( 'File' ) }: <b>{ fileName }</b>
</span>
<Grid columns={ 2 } gap={ 4 }>
<TextControl
__next40pxDefaultSize
__nextHasNoMarginBottom
onChange={ ( newLabel ) =>
onChange( {
...track,
label: newLabel,
} )
}
label={ __( 'Label' ) }
value={ label }
help={ __( 'Title of track' ) }
/>
<TextControl
__next40pxDefaultSize
__nextHasNoMarginBottom
onChange={ ( newSrcLang ) =>
onChange( {
...track,
srcLang: newSrcLang,
} )
}
label={ __( 'Source language' ) }
value={ srcLang }
help={ __( 'Language tag (en, fr, etc.)' ) }
/>
</Grid>
<VStack spacing="8">
<SelectControl
__next40pxDefaultSize
__nextHasNoMarginBottom
className="block-library-video-tracks-editor__single-track-editor-kind-select"
options={ KIND_OPTIONS }
value={ kind }
label={ __( 'Kind' ) }
onChange={ ( newKind ) => {
onChange( {
...track,
kind: newKind,
} );
} }
/>
<HStack className="block-library-video-tracks-editor__single-track-editor-buttons-container">
<Button
__next40pxDefaultSize
__nextHasNoMarginBottom
onChange={ ( newSrcLang ) =>
onChange( {
...track,
srcLang: newSrcLang,
} )
}
label={ __( 'Source language' ) }
value={ srcLang }
help={ __( 'Language tag (en, fr, etc.)' ) }
/>
</Grid>
<VStack spacing="8">
<SelectControl
isDestructive
variant="link"
onClick={ onRemove }
>
{ __( 'Remove track' ) }
</Button>
<Button
__next40pxDefaultSize
__nextHasNoMarginBottom
className="block-library-video-tracks-editor__single-track-editor-kind-select"
options={ KIND_OPTIONS }
value={ kind }
label={ __( 'Kind' ) }
onChange={ ( newKind ) => {
onChange( {
...track,
kind: newKind,
} );
variant="primary"
onClick={ () => {
const changes = {};
let hasChanges = false;
if ( label === '' ) {
changes.label = __( 'English' );
hasChanges = true;
}
if ( srcLang === '' ) {
changes.srcLang = 'en';
hasChanges = true;
}
if ( track.kind === undefined ) {
changes.kind = DEFAULT_KIND;
hasChanges = true;
}
if ( hasChanges ) {
onChange( {
...track,
...changes,
} );
}
onClose();
} }
/>
<HStack className="block-library-video-tracks-editor__single-track-editor-buttons-container">
<Button
__next40pxDefaultSize
variant="secondary"
onClick={ () => {
const changes = {};
let hasChanges = false;
if ( label === '' ) {
changes.label = __( 'English' );
hasChanges = true;
}
if ( srcLang === '' ) {
changes.srcLang = 'en';
hasChanges = true;
}
if ( track.kind === undefined ) {
changes.kind = DEFAULT_KIND;
hasChanges = true;
}
if ( hasChanges ) {
onChange( {
...track,
...changes,
} );
}
onClose();
} }
>
{ __( 'Close' ) }
</Button>
<Button
__next40pxDefaultSize
isDestructive
variant="link"
onClick={ onRemove }
>
{ __( 'Remove track' ) }
</Button>
</HStack>
</VStack>
>
{ __( 'Apply' ) }
</Button>
</HStack>
</VStack>
</NavigableMenu>
</VStack>
);
}

Expand All @@ -194,24 +179,44 @@ export default function TracksEditor( { tracks = [], onChange } ) {
return select( blockEditorStore ).getSettings().mediaUpload;
}, [] );
const [ trackBeingEdited, setTrackBeingEdited ] = useState( null );
const dropdownPopoverRef = useRef();

useEffect( () => {
dropdownPopoverRef.current?.focus();
}, [ trackBeingEdited ] );

if ( ! mediaUpload ) {
return null;
}
return (
<Dropdown
contentClassName="block-library-video-tracks-editor"
renderToggle={ ( { isOpen, onToggle } ) => (
<ToolbarGroup>
<ToolbarButton
aria-expanded={ isOpen }
aria-haspopup="true"
onClick={ onToggle }
>
{ __( 'Text tracks' ) }
</ToolbarButton>
</ToolbarGroup>
) }
focusOnMount
popoverProps={ {
ref: dropdownPopoverRef,
} }
renderToggle={ ( { isOpen, onToggle } ) => {
const handleOnToggle = () => {
if ( ! isOpen ) {
// When the Popover opens make sure the initial view is
// always the track list rather than the edit track UI.
setTrackBeingEdited( null );
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's better to do that in onClose Dropdown prop. And even also check if we need to update the state.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That would not work. onClose isn't called when closing the Popover with the Escape key. It is only called when activating the Close or remove buttons. As such, there's the need to make sure the UI is in the initial state when it opens.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hm.. I also tested pressing Escape and it is called. Did you try adding this in block-library-video-tracks-editor Dropdown?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK I see the misunderstanding. It's the SingleTrackEditor onClose that is not called when pressing Escape.
Instead, when adding the onClose to the Dropdown it is called, as expected. That could work.
However, I'm not sure I understand the problem you're willing to fix. Can you please expand a bit? Is it only a stylistic preference?
To me, things seem logical:

  • We need to make sure the UI is reset to the initial view (track list) when it opens.
  • As such, the state is reset when the UI opens.
  • I'm not sure there's the need to 'check if we need to update the state', as it should always be reset when it opens.

Copy link
Contributor

Choose a reason for hiding this comment

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

Logically it's the same, but semantically it makes more sense to me to reset the state in onClose. TBH I don't think it's that important..

Regarding the check, since we use the same value(null), it won't trigger a re-render, so we can skip that. At first I thought it would re-render.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the clarification and additional review @ntsekouras

}
onToggle();
};

return (
<ToolbarGroup>
<ToolbarButton
aria-expanded={ isOpen }
aria-haspopup="true"
onClick={ handleOnToggle }
>
{ __( 'Text tracks' ) }
</ToolbarButton>
</ToolbarGroup>
);
} }
renderContent={ () => {
if ( trackBeingEdited !== null ) {
return (
Expand All @@ -235,8 +240,21 @@ export default function TracksEditor( { tracks = [], onChange } ) {
/>
);
}

return (
<>
{ tracks.length === 0 && (
<div className="block-library-video-tracks-editor__tracks-informative-message">
Copy link
Contributor

Choose a reason for hiding this comment

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

Would using a VStack here simplify a bit the extra styles? Also block-library-video-tracks-editor__tracks-informative-message-description is not used anywhere.

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 block-library-video-tracks-editor__tracks-informative-message-description is not used anywhere.

it is used here to reduce the bottom margin.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would using a VStack here simplify a bit the extra styles?

I'd agree it could be simplified but it's out of the scope of this PR. As you mentioned, the whole UI of the video track editor feels a little 'old' and may benefit from some redesign, in a separate issue.

<h2 className="block-library-video-tracks-editor__tracks-informative-message-title">
{ __( 'Text tracks' ) }
</h2>
<p className="block-library-video-tracks-editor__tracks-informative-message-description">
{ __(
'Tracks can be subtitles, captions, chapters, or descriptions. They help make your content more accessible to a wider range of users.'
) }
</p>
Copy link
Contributor

Choose a reason for hiding this comment

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

In general these components might need a bit design refresh, but I think we should at least match trunk. That means we should add the separator that previously was added by the MenuGroup, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That separator is part of the components-menu-group MenuGroup component. It is meant to separate menu groups.

Previously, in the initial state, there were two groups and one of them was used incorrectly. Now, there's only one group so there's nothing to separate. After uploading a track, there are two groups and the separator is still there. Screenshot:

Screenshot 2024-11-18 at 14 22 40

</div>
) }
<NavigableMenu>
<TrackList
tracks={ tracks }
Expand Down
Loading