parent
1c6387a0a4
commit
f10e9dbcf3
|
@ -680,5 +680,12 @@ export default {
|
|||
statusOptions: 'Status options',
|
||||
confirm: 'Confirm',
|
||||
closeDialog: 'Close dialog',
|
||||
postPrivacy: 'Post privacy'
|
||||
postPrivacy: 'Post privacy',
|
||||
homeOnInstance: 'Home on {instance}',
|
||||
statusesTimelineOnInstance: 'Statuses: {timeline} timeline on {instance}',
|
||||
statusesHashtag: 'Statuses: #{hashtag} hashtag',
|
||||
statusesThread: 'Statuses: thread',
|
||||
statusesAccountTimeline: 'Statuses: account timeline',
|
||||
statusesList: 'Statuses: list',
|
||||
notificationsOnInstance: 'Notifications on {instance}'
|
||||
}
|
||||
|
|
5
src/routes/_components/DynamicHeading.html
Normal file
5
src/routes/_components/DynamicHeading.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
{#if level === 2}
|
||||
<h2 class={className || ''}><slot></slot></h2>
|
||||
{:else}
|
||||
<h1 class={className || ''}><slot></slot></h1>
|
||||
{/if}
|
|
@ -3,6 +3,7 @@
|
|||
without a div wrapper due to sticky-positioned compose button.
|
||||
TODO: this is a bit hacky due to code duplication
|
||||
-->
|
||||
<h1 class="sr-only">{headingLabel}</h1>
|
||||
<div class="timeline-home-page" aria-busy={hideTimeline}>
|
||||
{#if hidePage}
|
||||
<LoadingPage />
|
||||
|
@ -30,6 +31,7 @@
|
|||
import { store } from '../_store/store.js'
|
||||
import LoadingPage from './LoadingPage.html'
|
||||
import LazyComposeBox from './compose/LazyComposeBox.html'
|
||||
import { formatIntl } from '../_utils/formatIntl.js'
|
||||
|
||||
export default {
|
||||
oncreate () {
|
||||
|
@ -40,7 +42,8 @@
|
|||
},
|
||||
computed: {
|
||||
hidePage: ({ $timelineInitialized, $timelinePreinitialized }) => !$timelineInitialized && !$timelinePreinitialized,
|
||||
hideTimeline: ({ $timelineInitialized }) => !$timelineInitialized
|
||||
hideTimeline: ({ $timelineInitialized }) => !$timelineInitialized,
|
||||
headingLabel: ({ $currentInstance }) => formatIntl('intl.homeOnInstance', { instance: $currentInstance })
|
||||
},
|
||||
store: () => store,
|
||||
components: {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{#if realm === 'home'}
|
||||
<h1 class="sr-only">{intl.composeStatus}</h1>
|
||||
<h2 class="sr-only">{intl.composeStatus}</h2>
|
||||
{/if}
|
||||
<ComposeFileDrop {realm} >
|
||||
<div class="{computedClassName} {hideAndFadeIn}">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<h1 class="sr-only">{label}</h1>
|
||||
<DynamicHeading className="sr-only" level={headingLevel}>{label}</DynamicHeading>
|
||||
<FocusRestoration realm={focusRealm}>
|
||||
<div class="timeline" role="feed">
|
||||
{#if components}
|
||||
|
@ -26,6 +26,7 @@
|
|||
<ScrollListShortcuts />
|
||||
<script>
|
||||
import { store } from '../../_store/store.js'
|
||||
import DynamicHeading from '../DynamicHeading.html'
|
||||
import Status from '../status/Status.html'
|
||||
import LoadingFooter from './LoadingFooter.html'
|
||||
import MoreHeaderVirtualWrapper from './MoreHeaderVirtualWrapper.html'
|
||||
|
@ -51,6 +52,7 @@
|
|||
import { createMakeProps } from '../../_actions/createMakeProps.js'
|
||||
import { showMoreAndScrollToTop } from '../../_actions/showMoreAndScrollToTop.js'
|
||||
import FocusRestoration from '../FocusRestoration.html'
|
||||
import { formatIntl } from '../../_utils/formatIntl.js'
|
||||
|
||||
export default {
|
||||
oncreate () {
|
||||
|
@ -89,20 +91,23 @@
|
|||
),
|
||||
label: ({ timeline, $currentInstance, timelineType, timelineValue }) => {
|
||||
if (timelines[timeline]) {
|
||||
return `Statuses: ${timelines[timeline].label} timeline on ${$currentInstance}`
|
||||
return formatIntl('intl.statusesTimelineOnInstance', {
|
||||
timeline: timelines[timeline].label,
|
||||
instance: $currentInstance
|
||||
})
|
||||
}
|
||||
|
||||
switch (timelineType) {
|
||||
case 'tag':
|
||||
return `Statuses: #${timelineValue} hashtag`
|
||||
return formatIntl('intl.statusesHashtag', { hashtag: timelineValue })
|
||||
case 'status':
|
||||
return 'Statuses: thread'
|
||||
return 'intl.statusesThread'
|
||||
case 'account':
|
||||
return 'Statuses: account timeline'
|
||||
return 'intl.statusesAccountTimeline'
|
||||
case 'list':
|
||||
return 'Statuses: list'
|
||||
return 'intl.statusesList'
|
||||
case 'notifications':
|
||||
return `Notifications on ${$currentInstance}`
|
||||
return formatIntl('intl.notificationsOnInstance', { instance: $currentInstance })
|
||||
}
|
||||
},
|
||||
timelineType: ({ $currentTimelineType }) => $currentTimelineType,
|
||||
|
@ -127,7 +132,8 @@
|
|||
onClick: showMoreItemsForCurrentTimeline
|
||||
}
|
||||
},
|
||||
focusRealm: ({ $currentInstance, timeline }) => `${$currentInstance}-${timeline}`
|
||||
focusRealm: ({ $currentInstance, timeline }) => `${$currentInstance}-${timeline}`,
|
||||
headingLevel: ({ timeline, timelineType }) => timeline === 'home' || timelineType === 'status' ? 2 : 1
|
||||
},
|
||||
store: () => store,
|
||||
methods: {
|
||||
|
@ -232,7 +238,8 @@
|
|||
components: {
|
||||
ScrollListShortcuts,
|
||||
Shortcut,
|
||||
FocusRestoration
|
||||
FocusRestoration,
|
||||
DynamicHeading
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{#if $isUserLoggedIn}
|
||||
<h1 class="sr-only">{intl.community}</h1>
|
||||
<div class="community-page">
|
||||
|
||||
<FocusRestoration realm="community">
|
||||
<RadioGroup
|
||||
id="pinnables"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{#if $isUserLoggedIn}
|
||||
<h1 class="sr-only">{intl.search}</h1>
|
||||
<div class="search-page">
|
||||
<Search></Search>
|
||||
</div>
|
||||
|
|
52
tests/spec/042-headings.js
Normal file
52
tests/spec/042-headings.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import {
|
||||
settingsNavButton,
|
||||
notificationsNavButton,
|
||||
localTimelineNavButton,
|
||||
communityNavButton,
|
||||
searchNavButton,
|
||||
getNumElementsMatchingSelector,
|
||||
getUrl, getNthStatus
|
||||
} from '../utils'
|
||||
import { loginAsFoobar } from '../roles'
|
||||
|
||||
fixture`042-headings.js`
|
||||
.page`http://localhost:4002`
|
||||
|
||||
async function testHeadings (t, loggedIn) {
|
||||
const navButtons = [
|
||||
{ button: notificationsNavButton, url: 'notifications' },
|
||||
{ button: localTimelineNavButton, url: 'local' },
|
||||
{ button: communityNavButton, url: 'community' },
|
||||
{ button: searchNavButton, url: 'search' },
|
||||
{ button: settingsNavButton, url: 'settings' }
|
||||
]
|
||||
|
||||
// home page
|
||||
await t
|
||||
.expect(getNumElementsMatchingSelector('h1')()).eql(1)
|
||||
|
||||
if (loggedIn) {
|
||||
// status page
|
||||
await t
|
||||
.click(getNthStatus(1))
|
||||
.expect(getUrl()).contains('status')
|
||||
.expect(getNumElementsMatchingSelector('h1')()).eql(1)
|
||||
}
|
||||
|
||||
// non-home pages
|
||||
for (const { button, url } of navButtons) {
|
||||
await t
|
||||
.click(button)
|
||||
.expect(getUrl()).contains(url)
|
||||
.expect(getNumElementsMatchingSelector('h1')()).eql(1)
|
||||
}
|
||||
}
|
||||
|
||||
test('Only one <h1> when not logged in', async t => {
|
||||
await testHeadings(t, false)
|
||||
})
|
||||
|
||||
test('Only one <h1> when logged in', async t => {
|
||||
await loginAsFoobar(t)
|
||||
await testHeadings(t, true)
|
||||
})
|
|
@ -570,6 +570,12 @@ export function getNthPinnedStatusFavoriteButton (n) {
|
|||
return $(`${getNthPinnedStatusSelector(n)} .status-toolbar button:nth-child(3)`)
|
||||
}
|
||||
|
||||
export const getNumElementsMatchingSelector = (selector) => (exec(() => {
|
||||
return document.querySelectorAll(selector).length
|
||||
}, {
|
||||
dependencies: { selector }
|
||||
}))
|
||||
|
||||
export async function validateTimeline (t, timeline) {
|
||||
const timeout = 30000
|
||||
for (let i = 0; i < timeline.length; i++) {
|
||||
|
|
Loading…
Reference in a new issue