diff --git a/src/components/moderation/ContentHider.tsx b/src/components/moderation/ContentHider.tsx
index 525be31ca..bb4588b74 100644
--- a/src/components/moderation/ContentHider.tsx
+++ b/src/components/moderation/ContentHider.tsx
@@ -159,6 +159,7 @@ function ContentHiderActive({
control.open()
}
}}
+ accessibilityState={{expanded: override}}
label={desc.name}
accessibilityHint={
modui.noOverride
diff --git a/src/features/keyboardShortcuts/index.web.tsx b/src/features/keyboardShortcuts/index.web.tsx
index 05699ef81..5e17264ad 100644
--- a/src/features/keyboardShortcuts/index.web.tsx
+++ b/src/features/keyboardShortcuts/index.web.tsx
@@ -292,6 +292,7 @@ export function KeyboardShortcuts() {
if (
action === 'media' &&
(selected.dataset.keyboardNavigationPost ||
+ selected.dataset.keyboardNavigationWarning ||
selected.dataset.keyboardNavigationScope === 'messages')
) {
const targets = getPostMediaTargets(selected)
diff --git a/src/features/keyboardShortcuts/postNavigation.web.test.ts b/src/features/keyboardShortcuts/postNavigation.web.test.ts
index 486906dba..7794fd89e 100644
--- a/src/features/keyboardShortcuts/postNavigation.web.test.ts
+++ b/src/features/keyboardShortcuts/postNavigation.web.test.ts
@@ -616,13 +616,21 @@ it('reveals an embed warning before opening the quoted post', () => {
`
- const container = post.querySelector('[data-keyboard-navigation-embed-target]')!
+ const container = post.querySelector(
+ '[data-keyboard-navigation-embed-target]',
+ )!
const warning = container.querySelector('button')!
+ const onDetails = jest.fn()
+ container
+ .querySelector('[data-testid="warningDetails"]')!
+ .addEventListener('click', onDetails)
const onOpen = jest.fn()
warning.addEventListener('click', () => {
+ warning.setAttribute('aria-expanded', 'true')
const quote = document.createElement('button')
quote.dataset.testid = 'quotedPostOpenBtn'
quote.addEventListener('click', onOpen)
@@ -633,4 +641,80 @@ it('reveals an embed warning before opening the quoted post', () => {
expect(onOpen).not.toHaveBeenCalled()
expect(openPostMediaTarget(getPostMediaTargets(post)[0])).toBe(true)
expect(onOpen).toHaveBeenCalledTimes(1)
+ expect(onDetails).not.toHaveBeenCalled()
+})
+
+it.each(['quotedPostOpenBtn', 'postMediaOpenBtn', 'postEmbedOpenBtn'])(
+ 'opens %s before an unrelated post warning',
+ testID => {
+ document.body.replaceChildren()
+ const post = createPost({top: 20})
+ post.innerHTML = `
+
+
+
+
+ `
+ const warning = post.querySelector(
+ '[data-keyboard-navigation-warning]',
+ )!
+ const attachment = post.querySelector(
+ `[data-testid="${testID}"]`,
+ )!
+ const onOpen = jest.fn()
+ const onWarning = jest.fn()
+ attachment.addEventListener('click', onOpen)
+ warning.addEventListener('click', onWarning)
+
+ expect(getPostMediaTargets(post).map(target => target.control)).toEqual([
+ attachment,
+ ])
+ expect(clickPostAction(post, 'media')).toBe(true)
+ expect(onOpen).toHaveBeenCalledTimes(1)
+ expect(onWarning).not.toHaveBeenCalled()
+
+ attachment.remove()
+ expect(clickPostAction(post, 'media')).toBe(true)
+ expect(onWarning).toHaveBeenCalledTimes(1)
+ },
+)
+
+it('opens standalone warnings, including warnings that only show details', () => {
+ const warning = document.createElement('button')
+ warning.dataset.keyboardNavigationWarning = 'true'
+ document.body.appendChild(warning)
+ const onOpen = jest.fn()
+ warning.addEventListener('click', onOpen)
+
+ expect(clickPostAction(warning, 'media')).toBe(true)
+ expect(onOpen).toHaveBeenCalledTimes(1)
+ warning.disabled = true
+ expect(clickPostAction(warning, 'media')).toBe(false)
+})
+
+it('ignores revealed and hidden warnings inside embeds', () => {
+ const post = createPost({top: 20})
+ post.innerHTML = `
+
+ `
+ expect(getPostMediaTargets(post)).toEqual([])
+})
+
+it('opens a revealed quote before reaching warnings inside that quote', () => {
+ const post = createPost({top: 20})
+ post.innerHTML = `
+
+
+
+
+
+ `
+ expect(getPostMediaTargets(post).map(target => target.control)).toEqual([
+ post.querySelector('[data-testid="quotedPostOpenBtn"]'),
+ ])
})
diff --git a/src/features/keyboardShortcuts/postNavigation.web.ts b/src/features/keyboardShortcuts/postNavigation.web.ts
index 9278b3778..f2ea062e7 100644
--- a/src/features/keyboardShortcuts/postNavigation.web.ts
+++ b/src/features/keyboardShortcuts/postNavigation.web.ts
@@ -53,7 +53,9 @@ export function isPostVisible(element: HTMLElement) {
}
export function getNavigablePosts() {
- const items = Array.from(document.querySelectorAll(POST_SELECTOR))
+ const items = Array.from(
+ document.querySelectorAll(POST_SELECTOR),
+ )
.filter(isRendered)
.filter(
element =>
@@ -173,9 +175,10 @@ export function getPostMediaTargets(element: HTMLElement): PostMediaTarget[] {
const selectors = [
'[data-testid="quotedPostOpenBtn"]',
'[data-testid="postMediaOpenBtn"]',
- '.wsky-post__media [role="button"]',
'[data-keyboard-navigation-embed] iframe',
'[data-testid="postEmbedOpenBtn"]',
+ '[data-keyboard-navigation-warning]:not([aria-expanded="true"])',
+ '.wsky-post__media [role="button"]',
'[data-keyboard-navigation-embed] a[href], [data-keyboard-navigation-embed] [role="link"], [data-keyboard-navigation-embed] [role="button"]',
]
for (const container of containers) {
@@ -185,6 +188,9 @@ export function getPostMediaTargets(element: HTMLElement): PostMediaTarget[] {
).find(
candidate =>
isControlAvailable(candidate) &&
+ !candidate.closest('[aria-hidden="true"], [inert]') &&
+ (!candidate.hasAttribute('data-keyboard-navigation-warning') ||
+ candidate.getAttribute('aria-expanded') !== 'true') &&
(!boundaries.length ||
candidate.closest(EMBED_TARGET_SELECTOR) === container),
)
@@ -205,7 +211,21 @@ export function getPostMediaTargets(element: HTMLElement): PostMediaTarget[] {
break
}
}
- return targets
+ if (targets.length) return targets
+
+ const warnings = [
+ ...(element.matches('[data-keyboard-navigation-warning]') ? [element] : []),
+ ...element.querySelectorAll(
+ '[data-keyboard-navigation-warning]',
+ ),
+ ].filter(
+ control =>
+ control.getAttribute('aria-expanded') !== 'true' &&
+ !control.closest('[aria-hidden="true"], [inert]') &&
+ !control.closest('[data-testid="quotedPostOpenBtn"]') &&
+ isControlAvailable(control),
+ )
+ return warnings.map(control => ({kind: 'embed', control}))
}
function isControlAvailable(control: HTMLElement) {