Skip to content

Commit db1d28b

Browse files
committed
video - add aria-label parameter
1 parent 2943b16 commit db1d28b

File tree

3 files changed

+57
-13
lines changed

3 files changed

+57
-13
lines changed

news/changelog-1.9.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ All changes included in 1.9:
1919
### `html`
2020

2121
- ([#13413](https://github.com/quarto-dev/quarto-cli/issues/13413)): Fix uncentered play button in `video` shortcodes from cross-reference divs. (author: @bruvellu)
22+
- ([#13508](https://github.com/quarto-dev/quarto-cli/issues/13508)): Add `aria-label` support to `video` shortcode for improved accessibility.
2223

2324
### `typst`
2425

src/resources/extensions/quarto/video/_tests/test-suite.lua

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,15 @@ function TestYouTubeBuilder:testHeightWidth()
139139
checkYouTubeBuilder(params, expected)
140140
end
141141

142+
function TestYouTubeBuilder:testAriaLabel()
143+
local params = {
144+
src = 'https://www.youtube.com/embed/wo9vZccmqwc',
145+
ariaLabel = 'Video demonstration of features',
146+
}
147+
local expected = {snippet = '<iframe data-external="1" src="https://www.youtube.com/embed/wo9vZccmqwc" title="" aria-label="Video demonstration of features" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>', type = VIDEO_TYPES.YOUTUBE, src='https://www.youtube.com/embed/wo9vZccmqwc', videoId = 'wo9vZccmqwc'}
148+
checkYouTubeBuilder(params, expected)
149+
end
150+
142151
TestBrightcoveBuilder = {}
143152
local checkBrightcoveBuilder = function(params, expected)
144153
result = helpers.brightcoveBuilder(params)
@@ -206,6 +215,15 @@ function TestBrightcoveBuilder:testHeightWidth()
206215
checkBrightcoveBuilder(params, expected)
207216
end
208217

218+
function TestBrightcoveBuilder:testAriaLabel()
219+
local params = {
220+
src = 'https://players.brightcove.net/1460825906/default_default/index.html?videoId=5988531335001',
221+
ariaLabel = 'Video demonstration of features',
222+
}
223+
local expected = {snippet = '<iframe data-external="1" src="https://players.brightcove.net/1460825906/default_default/index.html?videoId=5988531335001" allowfullscreen="" title="" aria-label="Video demonstration of features" allow="encrypted-media"></iframe>', type = VIDEO_TYPES.BRIGHTCOVE, src='https://players.brightcove.net/1460825906/default_default/index.html?videoId=5988531335001' }
224+
checkBrightcoveBuilder(params, expected)
225+
end
226+
209227
TestVimeoBuilder = {}
210228
local checkVimeoBuilder = function(params, expected)
211229
result = helpers.vimeoBuilder(params)
@@ -288,6 +306,15 @@ function TestVimeoBuilder:testHeightWidth()
288306
checkVimeoBuilder(params, expected)
289307
end
290308

309+
function TestVimeoBuilder:testAriaLabel()
310+
local params = {
311+
src = 'https://vimeo.com/548291210',
312+
ariaLabel = 'Video demonstration of features',
313+
}
314+
local expected = {snippet = '<iframe data-external="1" src="https://player.vimeo.com/video/548291210" frameborder="0" title="" aria-label="Video demonstration of features" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>', type = VIDEO_TYPES.VIMEO, src='https://player.vimeo.com/video/548291210', videoId = '548291210' }
315+
checkVimeoBuilder(params, expected)
316+
end
317+
291318
TestVideoJSBuilder = {}
292319
local checkVideoJSBuilder = function(params, expected)
293320
VIDEO_SHORTCODE_NUM_VIDEOJS = 0 -- Reset Counter
@@ -368,6 +395,20 @@ function TestVideoJSBuilder:testHeightWidth()
368395
checkVideoJSBuilder(params, expected)
369396
end
370397

398+
function TestVideoJSBuilder:testAriaLabel()
399+
local params = {
400+
ariaLabel = 'Video demonstration of features',
401+
src = './intro-cern.mp4'
402+
}
403+
local expected = {
404+
snippet="<video id=\"video_shortcode_videojs_video1\" class=\"video-js vjs-default-skin vjs-big-play-centered vjs-fluid\" controls preload=\"auto\" data-setup='{}' title=\"\" aria-label=\"Video demonstration of features\"><source src=\"./intro-cern.mp4\"></video>",
405+
type="VIDEOJS",
406+
src='./intro-cern.mp4',
407+
id="video_shortcode_videojs_video1"
408+
}
409+
checkVideoJSBuilder(params, expected)
410+
end
411+
371412
TestVideoResponsive = {}
372413
function TestVideoResponsive:testNoResponsive()
373414
result = helpers.wrapWithDiv('fake-to-wrap')

src/resources/extensions/quarto/video/video.lua

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ local replaceCommonAttributes = function(snippet, params)
6666
height = params.height and ' height="' .. params.height .. '"' or '',
6767
width = params.width and ' width="' .. params.width .. '"' or '',
6868
title = params.title or '',
69+
ariaLabel = params.ariaLabel and ' aria-label="' .. params.ariaLabel .. '"' or '',
6970
}
7071
return result
7172
end
@@ -89,7 +90,7 @@ local youTubeBuilder = function(params)
8990
local YOUTUBE_EMBED = 'https://www.youtube.com/embed/'
9091
params.src = YOUTUBE_EMBED .. match
9192

92-
local SNIPPET = [[<iframe data-external="1" src="{src}{start}"{width}{height} title="{title}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>]]
93+
local SNIPPET = [[<iframe data-external="1" src="{src}{start}"{width}{height} title="{title}"{ariaLabel} frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>]]
9394
snippet = replaceCommonAttributes(SNIPPET, params)
9495

9596
local result = {}
@@ -116,7 +117,7 @@ local brightcoveBuilder = function(params)
116117

117118
local result = {}
118119

119-
local SNIPPET = [[<iframe data-external="1" src="{src}"{width}{height} allowfullscreen="" title="{title}" allow="encrypted-media"></iframe>]]
120+
local SNIPPET = [[<iframe data-external="1" src="{src}"{width}{height} allowfullscreen="" title="{title}"{ariaLabel} allow="encrypted-media"></iframe>]]
120121
result.snippet = replaceCommonAttributes(SNIPPET, params)
121122
result.type = VIDEO_TYPES.BRIGHTCOVE
122123
result.src = params.src
@@ -143,7 +144,7 @@ local vimeoBuilder = function(params)
143144
end
144145

145146

146-
local SNIPPET = [[<iframe data-external="1" src="{src}"{width}{height} frameborder="0" title="{title}" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>]]
147+
local SNIPPET = [[<iframe data-external="1" src="{src}"{width}{height} frameborder="0" title="{title}"{ariaLabel} allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>]]
147148

148149
local result = {}
149150

@@ -160,7 +161,7 @@ local videoJSBuilder = function(params)
160161
VIDEO_SHORTCODE_NUM_VIDEOJS = VIDEO_SHORTCODE_NUM_VIDEOJS + 1
161162
local id = "video_shortcode_videojs_video" .. VIDEO_SHORTCODE_NUM_VIDEOJS
162163

163-
local SNIPPET = [[<video id="{id}"{width}{height} class="video-js vjs-default-skin vjs-big-play-centered {fluid}" controls preload="auto" data-setup='{}' title="{title}"><source src="{src}"></video>]]
164+
local SNIPPET = [[<video id="{id}"{width}{height} class="video-js vjs-default-skin vjs-big-play-centered {fluid}" controls preload="auto" data-setup='{}' title="{title}"{ariaLabel}><source src="{src}"></video>]]
164165
local snippet = params.snippet or SNIPPET
165166
snippet = replaceCommonAttributes(snippet, params)
166167
snippet = interpolate {
@@ -176,14 +177,14 @@ local videoJSBuilder = function(params)
176177
result.id = id
177178
return result
178179
end
179-
local getSnippetFromBuilders = function(src, height, width, title, start)
180+
local getSnippetFromBuilders = function(src, height, width, title, start, ariaLabel)
180181
local builderList = {
181182
youTubeBuilder,
182183
brightcoveBuilder,
183184
vimeoBuilder,
184185
videoJSBuilder}
185186

186-
local params = { src = src, height = height, width = width, title = title, start = start }
187+
local params = { src = src, height = height, width = width, title = title, start = start, ariaLabel = ariaLabel }
187188

188189
for i = 1, #builderList do
189190
local builtSnippet = builderList[i](params)
@@ -211,12 +212,12 @@ function formatAsciiDocVideo(src, type)
211212
return 'video::' .. src .. '[' .. type .. ']'
212213
end
213214

214-
local function asciidocVideo(src, height, width, title, start, _aspectRatio)
215+
local function asciidocVideo(src, height, width, title, start, _aspectRatio, ariaLabel)
215216
local asciiDocVideoRawBlock = function(src, type)
216217
return pandoc.RawBlock("asciidoc", formatAsciiDocVideo(src, type) .. '\n\n')
217218
end
218219

219-
local videoSnippetAndType = getSnippetFromBuilders(src, height, width, title, start)
220+
local videoSnippetAndType = getSnippetFromBuilders(src, height, width, title, start, ariaLabel)
220221
if videoSnippetAndType.type == VIDEO_TYPES.YOUTUBE then
221222
-- Use the video id to form an asciidoc video
222223
if videoSnippetAndType.videoId ~= nil then
@@ -233,13 +234,13 @@ local function asciidocVideo(src, height, width, title, start, _aspectRatio)
233234

234235
end
235236

236-
function htmlVideo(src, height, width, title, start, aspectRatio)
237+
function htmlVideo(src, height, width, title, start, aspectRatio, ariaLabel)
237238

238239
-- https://github.com/quarto-dev/quarto-cli/issues/6833
239240
-- handle partially-specified width, height, and aspectRatio
240241
if aspectRatio then
241242
-- https://github.com/quarto-dev/quarto-cli/issues/11699#issuecomment-2549219533
242-
-- we remove quotes as a
243+
-- we remove quotes as a
243244
-- local workaround for inconsistent shortcode argument parsing on our end.
244245
--
245246
-- removing quotes in general is not a good idea, but the
@@ -256,7 +257,7 @@ function htmlVideo(src, height, width, title, start, aspectRatio)
256257
end
257258
end
258259

259-
local videoSnippetAndType = getSnippetFromBuilders(src, height, width, title, start)
260+
local videoSnippetAndType = getSnippetFromBuilders(src, height, width, title, start, ariaLabel)
260261
local videoSnippet
261262

262263
videoSnippet = videoSnippetAndType.snippet
@@ -326,6 +327,7 @@ return {
326327
local heightValue = checkArg(kwargs, 'height')
327328
local widthValue = checkArg(kwargs, 'width')
328329
local aspectRatio = checkArg(kwargs, 'aspectRatio')
330+
local ariaLabelValue = checkArg(kwargs, 'aria-label')
329331

330332
if isEmpty(aspectRatio) then
331333
aspectRatio = checkArg(kwargs, 'aspect-ratio')
@@ -343,9 +345,9 @@ return {
343345
end
344346

345347
if quarto.doc.is_format("html:js") then
346-
return htmlVideo(srcValue, heightValue, widthValue, titleValue, startValue, aspectRatio)
348+
return htmlVideo(srcValue, heightValue, widthValue, titleValue, startValue, aspectRatio, ariaLabelValue)
347349
elseif quarto.doc.is_format("asciidoc") then
348-
return asciidocVideo(srcValue, heightValue, widthValue, titleValue, startValue, aspectRatio)
350+
return asciidocVideo(srcValue, heightValue, widthValue, titleValue, startValue, aspectRatio, ariaLabelValue)
349351
elseif quarto.doc.is_format("markdown") then
350352
if srcValue:sub(1, 4) == "http" then
351353
-- For remote videos, we can emit a link

0 commit comments

Comments
 (0)