parent
1c6387a0a4
commit
f10e9dbcf3
|
@ -680,5 +680,12 @@ export default {
|
||||||
statusOptions: 'Status options',
|
statusOptions: 'Status options',
|
||||||
confirm: 'Confirm',
|
confirm: 'Confirm',
|
||||||
closeDialog: 'Close dialog',
|
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.
|
without a div wrapper due to sticky-positioned compose button.
|
||||||
TODO: this is a bit hacky due to code duplication
|
TODO: this is a bit hacky due to code duplication
|
||||||
-->
|
-->
|
||||||
|
<h1 class="sr-only">{headingLabel}</h1>
|
||||||
<div class="timeline-home-page" aria-busy={hideTimeline}>
|
<div class="timeline-home-page" aria-busy={hideTimeline}>
|
||||||
{#if hidePage}
|
{#if hidePage}
|
||||||
<LoadingPage />
|
<LoadingPage />
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
import { store } from '../_store/store.js'
|
import { store } from '../_store/store.js'
|
||||||
import LoadingPage from './LoadingPage.html'
|
import LoadingPage from './LoadingPage.html'
|
||||||
import LazyComposeBox from './compose/LazyComposeBox.html'
|
import LazyComposeBox from './compose/LazyComposeBox.html'
|
||||||
|
import { formatIntl } from '../_utils/formatIntl.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
oncreate () {
|
oncreate () {
|
||||||
|
@ -40,7 +42,8 @@
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hidePage: ({ $timelineInitialized, $timelinePreinitialized }) => !$timelineInitialized && !$timelinePreinitialized,
|
hidePage: ({ $timelineInitialized, $timelinePreinitialized }) => !$timelineInitialized && !$timelinePreinitialized,
|
||||||
hideTimeline: ({ $timelineInitialized }) => !$timelineInitialized
|
hideTimeline: ({ $timelineInitialized }) => !$timelineInitialized,
|
||||||
|
headingLabel: ({ $currentInstance }) => formatIntl('intl.homeOnInstance', { instance: $currentInstance })
|
||||||
},
|
},
|
||||||
store: () => store,
|
store: () => store,
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{#if realm === 'home'}
|
{#if realm === 'home'}
|
||||||
<h1 class="sr-only">{intl.composeStatus}</h1>
|
<h2 class="sr-only">{intl.composeStatus}</h2>
|
||||||
{/if}
|
{/if}
|
||||||
<ComposeFileDrop {realm} >
|
<ComposeFileDrop {realm} >
|
||||||
<div class="{computedClassName} {hideAndFadeIn}">
|
<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}>
|
<FocusRestoration realm={focusRealm}>
|
||||||
<div class="timeline" role="feed">
|
<div class="timeline" role="feed">
|
||||||
{#if components}
|
{#if components}
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
<ScrollListShortcuts />
|
<ScrollListShortcuts />
|
||||||
<script>
|
<script>
|
||||||
import { store } from '../../_store/store.js'
|
import { store } from '../../_store/store.js'
|
||||||
|
import DynamicHeading from '../DynamicHeading.html'
|
||||||
import Status from '../status/Status.html'
|
import Status from '../status/Status.html'
|
||||||
import LoadingFooter from './LoadingFooter.html'
|
import LoadingFooter from './LoadingFooter.html'
|
||||||
import MoreHeaderVirtualWrapper from './MoreHeaderVirtualWrapper.html'
|
import MoreHeaderVirtualWrapper from './MoreHeaderVirtualWrapper.html'
|
||||||
|
@ -51,6 +52,7 @@
|
||||||
import { createMakeProps } from '../../_actions/createMakeProps.js'
|
import { createMakeProps } from '../../_actions/createMakeProps.js'
|
||||||
import { showMoreAndScrollToTop } from '../../_actions/showMoreAndScrollToTop.js'
|
import { showMoreAndScrollToTop } from '../../_actions/showMoreAndScrollToTop.js'
|
||||||
import FocusRestoration from '../FocusRestoration.html'
|
import FocusRestoration from '../FocusRestoration.html'
|
||||||
|
import { formatIntl } from '../../_utils/formatIntl.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
oncreate () {
|
oncreate () {
|
||||||
|
@ -89,20 +91,23 @@
|
||||||
),
|
),
|
||||||
label: ({ timeline, $currentInstance, timelineType, timelineValue }) => {
|
label: ({ timeline, $currentInstance, timelineType, timelineValue }) => {
|
||||||
if (timelines[timeline]) {
|
if (timelines[timeline]) {
|
||||||
return `Statuses: ${timelines[timeline].label} timeline on ${$currentInstance}`
|
return formatIntl('intl.statusesTimelineOnInstance', {
|
||||||
|
timeline: timelines[timeline].label,
|
||||||
|
instance: $currentInstance
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (timelineType) {
|
switch (timelineType) {
|
||||||
case 'tag':
|
case 'tag':
|
||||||
return `Statuses: #${timelineValue} hashtag`
|
return formatIntl('intl.statusesHashtag', { hashtag: timelineValue })
|
||||||
case 'status':
|
case 'status':
|
||||||
return 'Statuses: thread'
|
return 'intl.statusesThread'
|
||||||
case 'account':
|
case 'account':
|
||||||
return 'Statuses: account timeline'
|
return 'intl.statusesAccountTimeline'
|
||||||
case 'list':
|
case 'list':
|
||||||
return 'Statuses: list'
|
return 'intl.statusesList'
|
||||||
case 'notifications':
|
case 'notifications':
|
||||||
return `Notifications on ${$currentInstance}`
|
return formatIntl('intl.notificationsOnInstance', { instance: $currentInstance })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
timelineType: ({ $currentTimelineType }) => $currentTimelineType,
|
timelineType: ({ $currentTimelineType }) => $currentTimelineType,
|
||||||
|
@ -127,7 +132,8 @@
|
||||||
onClick: showMoreItemsForCurrentTimeline
|
onClick: showMoreItemsForCurrentTimeline
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
focusRealm: ({ $currentInstance, timeline }) => `${$currentInstance}-${timeline}`
|
focusRealm: ({ $currentInstance, timeline }) => `${$currentInstance}-${timeline}`,
|
||||||
|
headingLevel: ({ timeline, timelineType }) => timeline === 'home' || timelineType === 'status' ? 2 : 1
|
||||||
},
|
},
|
||||||
store: () => store,
|
store: () => store,
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -232,7 +238,8 @@
|
||||||
components: {
|
components: {
|
||||||
ScrollListShortcuts,
|
ScrollListShortcuts,
|
||||||
Shortcut,
|
Shortcut,
|
||||||
FocusRestoration
|
FocusRestoration,
|
||||||
|
DynamicHeading
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{#if $isUserLoggedIn}
|
{#if $isUserLoggedIn}
|
||||||
|
<h1 class="sr-only">{intl.community}</h1>
|
||||||
<div class="community-page">
|
<div class="community-page">
|
||||||
|
|
||||||
<FocusRestoration realm="community">
|
<FocusRestoration realm="community">
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
id="pinnables"
|
id="pinnables"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{#if $isUserLoggedIn}
|
{#if $isUserLoggedIn}
|
||||||
|
<h1 class="sr-only">{intl.search}</h1>
|
||||||
<div class="search-page">
|
<div class="search-page">
|
||||||
<Search></Search>
|
<Search></Search>
|
||||||
</div>
|
</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)`)
|
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) {
|
export async function validateTimeline (t, timeline) {
|
||||||
const timeout = 30000
|
const timeout = 30000
|
||||||
for (let i = 0; i < timeline.length; i++) {
|
for (let i = 0; i < timeline.length; i++) {
|
||||||
|
|
Loading…
Reference in a new issue