add auto-focus to reply input text
This commit is contained in:
		
							parent
							
								
									9a0071a934
								
							
						
					
					
						commit
						ed0db17ca0
					
				
					 7 changed files with 40 additions and 4 deletions
				
			
		| 
						 | 
					@ -4,7 +4,8 @@
 | 
				
			||||||
          aria-pressed="{{pressable ? !!pressed : ''}}"
 | 
					          aria-pressed="{{pressable ? !!pressed : ''}}"
 | 
				
			||||||
          class="{{computedClass}}"
 | 
					          class="{{computedClass}}"
 | 
				
			||||||
          :disabled
 | 
					          :disabled
 | 
				
			||||||
          delegate-key="{{delegateKey}}" >
 | 
					          delegate-key="{{delegateKey}}"
 | 
				
			||||||
 | 
					          focus-key="{{focusKey || ''}}" >
 | 
				
			||||||
    <svg class="icon-button-svg">
 | 
					    <svg class="icon-button-svg">
 | 
				
			||||||
      <use xlink:href="{{href}}" />
 | 
					      <use xlink:href="{{href}}" />
 | 
				
			||||||
    </svg>
 | 
					    </svg>
 | 
				
			||||||
| 
						 | 
					@ -14,6 +15,7 @@
 | 
				
			||||||
          aria-label="{{label}}"
 | 
					          aria-label="{{label}}"
 | 
				
			||||||
          aria-pressed="{{pressable ? !!pressed : ''}}"
 | 
					          aria-pressed="{{pressable ? !!pressed : ''}}"
 | 
				
			||||||
          class="{{computedClass}}"
 | 
					          class="{{computedClass}}"
 | 
				
			||||||
 | 
					          focus-key="{{focusKey || ''}}"
 | 
				
			||||||
          :disabled
 | 
					          :disabled
 | 
				
			||||||
          on:click >
 | 
					          on:click >
 | 
				
			||||||
    <svg class="icon-button-svg">
 | 
					    <svg class="icon-button-svg">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@
 | 
				
			||||||
      <ComposeContentWarning :realm :contentWarning />
 | 
					      <ComposeContentWarning :realm :contentWarning />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  {{/if}}
 | 
					  {{/if}}
 | 
				
			||||||
  <ComposeInput :realm :text />
 | 
					  <ComposeInput :realm :text :autoFocus />
 | 
				
			||||||
  <ComposeLengthGauge :length :overLimit />
 | 
					  <ComposeLengthGauge :length :overLimit />
 | 
				
			||||||
  <ComposeToolbar :realm :postPrivacy :media :contentWarningShown />
 | 
					  <ComposeToolbar :realm :postPrivacy :media :contentWarningShown />
 | 
				
			||||||
  <ComposeLengthIndicator :length :overLimit />
 | 
					  <ComposeLengthIndicator :length :overLimit />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,6 +43,9 @@
 | 
				
			||||||
      setupSyncFromStore() {
 | 
					      setupSyncFromStore() {
 | 
				
			||||||
        this.observe('text', text => {
 | 
					        this.observe('text', text => {
 | 
				
			||||||
          this.set({rawText: text})
 | 
					          this.set({rawText: text})
 | 
				
			||||||
 | 
					          if (this.get('autoFocus')) {
 | 
				
			||||||
 | 
					            this.refs.textarea.focus()
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      setupSyncToStore() {
 | 
					      setupSyncToStore() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@
 | 
				
			||||||
    href="#fa-reply"
 | 
					    href="#fa-reply"
 | 
				
			||||||
    disabled="{{disableReply}}"
 | 
					    disabled="{{disableReply}}"
 | 
				
			||||||
    delegateKey="{{replyKey}}"
 | 
					    delegateKey="{{replyKey}}"
 | 
				
			||||||
 | 
					    focusKey="{{replyKey}}"
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
  <IconButton
 | 
					  <IconButton
 | 
				
			||||||
    label="{{reblogLabel}}"
 | 
					    label="{{reblogLabel}}"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@
 | 
				
			||||||
            timelineValue="{{params.statusId}}"
 | 
					            timelineValue="{{params.statusId}}"
 | 
				
			||||||
            :status
 | 
					            :status
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
    <ComposeBox realm="{{params.statusId}}" />
 | 
					    <ComposeBox realm="{{params.statusId}}" autoFocus="true" />
 | 
				
			||||||
  {{else}}
 | 
					  {{else}}
 | 
				
			||||||
    <LoadingPage />
 | 
					    <LoadingPage />
 | 
				
			||||||
  {{/if}}
 | 
					  {{/if}}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  getNthStatus, scrollToStatus, closeDialogButton, modalDialogContents, getActiveElementClass, goBack, getUrl,
 | 
					  getNthStatus, scrollToStatus, closeDialogButton, modalDialogContents, getActiveElementClass, goBack, getUrl,
 | 
				
			||||||
  goBackButton, getActiveElementInnerText
 | 
					  goBackButton, getActiveElementInnerText, getNthReplyButton, getActiveElementAriaLabel, getActiveElementInsideNthStatus
 | 
				
			||||||
} from '../utils'
 | 
					} from '../utils'
 | 
				
			||||||
import { foobarRole } from '../roles'
 | 
					import { foobarRole } from '../roles'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ test('modal preserves focus', async t => {
 | 
				
			||||||
    .click(closeDialogButton)
 | 
					    .click(closeDialogButton)
 | 
				
			||||||
    .expect(modalDialogContents.exists).notOk()
 | 
					    .expect(modalDialogContents.exists).notOk()
 | 
				
			||||||
    .expect(getActiveElementClass()).contains('play-video-button')
 | 
					    .expect(getActiveElementClass()).contains('play-video-button')
 | 
				
			||||||
 | 
					    .expect(getActiveElementInsideNthStatus()).eql('9')
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test('timeline preserves focus', async t => {
 | 
					test('timeline preserves focus', async t => {
 | 
				
			||||||
| 
						 | 
					@ -24,6 +25,7 @@ test('timeline preserves focus', async t => {
 | 
				
			||||||
  await goBack()
 | 
					  await goBack()
 | 
				
			||||||
  await t.expect(getUrl()).eql('http://localhost:4002/')
 | 
					  await t.expect(getUrl()).eql('http://localhost:4002/')
 | 
				
			||||||
    .expect(getActiveElementClass()).contains('status-article status-in-timeline')
 | 
					    .expect(getActiveElementClass()).contains('status-article status-in-timeline')
 | 
				
			||||||
 | 
					    .expect(getActiveElementInsideNthStatus()).eql('0')
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test('timeline link preserves focus', async t => {
 | 
					test('timeline link preserves focus', async t => {
 | 
				
			||||||
| 
						 | 
					@ -38,6 +40,7 @@ test('timeline link preserves focus', async t => {
 | 
				
			||||||
    .click(goBackButton)
 | 
					    .click(goBackButton)
 | 
				
			||||||
    .expect(getUrl()).eql('http://localhost:4002/')
 | 
					    .expect(getUrl()).eql('http://localhost:4002/')
 | 
				
			||||||
    .expect(getActiveElementClass()).contains('status-sidebar')
 | 
					    .expect(getActiveElementClass()).contains('status-sidebar')
 | 
				
			||||||
 | 
					    .expect(getActiveElementInsideNthStatus()).eql('0')
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test('notification timeline preserves focus', async t => {
 | 
					test('notification timeline preserves focus', async t => {
 | 
				
			||||||
| 
						 | 
					@ -49,4 +52,17 @@ test('notification timeline preserves focus', async t => {
 | 
				
			||||||
    .click(goBackButton)
 | 
					    .click(goBackButton)
 | 
				
			||||||
    .expect(getUrl()).eql('http://localhost:4002/notifications')
 | 
					    .expect(getUrl()).eql('http://localhost:4002/notifications')
 | 
				
			||||||
    .expect(getActiveElementInnerText()).eql('quux')
 | 
					    .expect(getActiveElementInnerText()).eql('quux')
 | 
				
			||||||
 | 
					    .expect(getActiveElementInsideNthStatus()).eql('5')
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('reply preserves focus and moves focus to the text input', async t => {
 | 
				
			||||||
 | 
					  await t.useRole(foobarRole)
 | 
				
			||||||
 | 
					    .click(getNthReplyButton(1))
 | 
				
			||||||
 | 
					    .expect(getUrl()).contains('/reply')
 | 
				
			||||||
 | 
					    .expect(getActiveElementClass()).contains('compose-box-input')
 | 
				
			||||||
 | 
					    .click(goBackButton)
 | 
				
			||||||
 | 
					    .expect(getUrl()).eql('http://localhost:4002/')
 | 
				
			||||||
 | 
					    .expect(getActiveElementClass()).contains('icon-button')
 | 
				
			||||||
 | 
					    .expect(getActiveElementAriaLabel()).eql('Reply')
 | 
				
			||||||
 | 
					    .expect(getActiveElementInsideNthStatus()).eql('1')
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,6 +53,20 @@ export const getActiveElementInnerText = exec(() =>
 | 
				
			||||||
  document.activeElement && document.activeElement.innerText
 | 
					  document.activeElement && document.activeElement.innerText
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getActiveElementAriaLabel = exec(() =>
 | 
				
			||||||
 | 
					  document.activeElement ? document.activeElement.getAttribute('aria-label') : ''
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getActiveElementInsideNthStatus = exec(() => {
 | 
				
			||||||
 | 
					  let element = document.activeElement
 | 
				
			||||||
 | 
					  while (element) {
 | 
				
			||||||
 | 
					    if (element.hasAttribute('aria-posinset')) {
 | 
				
			||||||
 | 
					      return element.getAttribute('aria-posinset')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    element = element.parentElement
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const goBack = exec(() => window.history.back())
 | 
					export const goBack = exec(() => window.history.back())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const forceOffline = exec(() => window.store.set({online: false}))
 | 
					export const forceOffline = exec(() => window.store.set({online: false}))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue