loading...
Cover image for Forem: Keyboard shortcuts

Forem: Keyboard shortcuts

link2twenty profile image Andrew Bone ・1 min read

It's now possible to 'easily' add keyboard shortcuts to components used in forem. Some already exist like pressing / to focus on the search bar or pressing 0 to enter 'zen' mode on the feed.

Also feed navigation is coming

A11y: Add keyboard navigation to article feed #10468

What type of PR is this? (check all applicable)

  • [ ] Refactor
  • [x] Feature
  • [ ] Bug Fix
  • [ ] Optimization
  • [ ] Documentation Update

Description

This adds keyboard navigation to the article feed (and potentially more stuff).

Related Tickets & Documents

Closes #596

QA Instructions, Screenshots, Recordings

Keys

  1. j goes down
  2. k goes up

Behavior

If no article is focused on (or anything inside an article), the first article that is visible in the feed is focused on

Demo

demo

Added tests?

  • [x] yes
  • [ ] no, because they aren't needed
  • [ ] no, because I need help

Added to documentation?

  • [ ] docs.forem.com
  • [ ] readme
  • [x] no documentation needed

JSDoc added

How it works

The hook can be called in any functional component.

useGlobalListNavigation(
  'article[id=featured-story-marker],article[id^=article-]',
  'a[id^=article-link-]',
  'div.paged-stories',
);
Enter fullscreen mode Exit fullscreen mode

3 params

  1. itemContainerSelector: The selector for the highest level of the list item
  2. focusableSelector: What should actually be focused on in the list item container
  3. waterfallItemContainerSelector (optional): selector of the waterfall container if the list of items uses a waterfall-like architecture, like the article feed.

Flow

It uses the new global key event listener hook (which is now used by the search bar for / and 0) to listen for j and k.

On the first keypress, it focuses on the first visible article. Then it follows one of the next paths:

Happy path:

  1. Get closest parent article of the currently focused-on element
  2. Get next or previous sibling
  3. Get the link of sibling
  4. Focus on link

Dealing with the article feed waterfall

What I refer to as the waterfall:

<article></article>
<article></article>
<article></article>
<div class="paged-stories">
  <article></article>
  <article></article>
  <article></article>
  <div class="paged-stories">
    <article></article>
    <article></article>
    <article></article>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode
Going down the waterfall
  1. Get closest parent article of the currently focused-on element
  2. Get next sibling (is a paged-stories div)
  3. Get the next article link in DOM (article link of the first article in the div)
  4. Focus on the article link
Going up the waterfall
  1. Get closest parent article of the currently focused-on element
  2. Get previous sibling (there is none)
  3. Get parent paged-stories div
  4. Get previous sibling (is an article)
  5. Get the article link
  6. Focus on the article link

Are there any keyboard shortcuts you'd like to see? Make some suggestions in the comments or make a feature request directly on the repo.

PR to add keyboard shortcuts:

If you're interested in the hook that is used to make keyboard shortcuts easy it's here.

Feel free to explore it and even use it to add your own shortcuts (once the team accepts the feature request).

Allow keyboard shortcuts #10713

What type of PR is this? (check all applicable)

  • [ ] Refactor
  • [x] Feature
  • [ ] Bug Fix
  • [ ] Optimization
  • [ ] Documentation Update

Description

Add handler for keyboard shortcuts. This allows custom shortcuts to be set up on a page by page (or even component by component) basis.

React demo

Usage

In this example we're going to run a function when the user presses CTRL+SHIFT+P

const shortcuts = {
  "ctrl+shift+KeyP":  (e) => {
    e.preventDefault();
    someFunction();
  }
}

<KeyboardShortcuts shortcuts={shortcuts} />
Enter fullscreen mode Exit fullscreen mode

Related Tickets & Documents

related: #5023 #596

QA Instructions, Screenshots, Recordings

This facilitates changes in the future

Added tests?

  • [x] yes
  • [ ] no, because they aren't needed
  • [ ] no, because I need help

Added to documentation?

  • [ ] docs.forem.com
  • [ ] readme
  • [x] no documentation needed

[optional] Are there any post deployment tasks we need to perform?

Once this is merged I'd like to modify this current shortcut to use this method before thinking about what other shortcuts could/should be added.

[optional] What gif best describes this PR or how it makes you feel?

seeing the code

Discussion

pic
Editor guide
Collapse
moopet profile image
Ben Sinclair

My suggestion is to surface these to the user. They're not discoverable - who would guess that you could switch between two layouts on one particular type of page by pressing "0" as long as an input field isn't highlighted?

They're nice things to have, but if the only way people find out is by accident they're going to miss out at best, and be confused at worst. Imagine pressing 0 one time and wondering why the site doesn't always look the same from one browser/day to the next.

I did try to add a comment over on forem, but it doesn't work: the submit button is glitchy and I can't click on anything!

forem comment with glitching controls

Collapse
link2twenty profile image
Andrew Bone Author

I 100% agree, part of the idea behind this change was to make all shortcuts discoverable (in the code at least) rather than all being implemented however and wherever. We have an issue open, by @reobin , where we're looking at how best to show users shortcuts and to document them programmatically so we don't have to make and maintain a large help file.

feat: Add a key shortcuts modal #10879

#10713 added a keyboard shortcut hook, and the migration for the current keyboard shortcuts to use the new hook has started. See #10878

Describe the solution you'd like

As suggested in #10713, the new hook could be used to populate a keyboard shortcut modal like GitHub's one:

Screen Shot 2020-10-16 at 08 54 53

The key ? would pop up that modal.

The details of the implementation have to be discussed. Using the hook to populate a keyboard shortcut store means that the modal would be different on a per-page basis depending on which shortcuts are used on the current page.

Also, they can't be added in there randomly. The shortcuts would need categorization, so the hook will probably need an update in order to receive the shortcuts categories.

Describe alternatives you've considered

It would be easier to just build a static modal, although less maintainable.

Additional context

#10468 #10713 #10878

Collapse
reobin profile image
Robin Gagnon

Once there is the navigation for the article feed, the bookmark shortcut is a must.

Throwing ideas below:

  • Write a new post (n?)
  • Go to profile (p?)
  • Go to listings (l?)
  • Go back to the feed (f?)
  • Maybe shortcuts to switch between the different feeds (week, month, etc.)
Collapse
link2twenty profile image
Andrew Bone Author

I like those maybe add message (M) too?

Twitter use chaining for certain actions like going to a certain page is g then another letter which is something we can theoretically do with the hook.

Collapse
reobin profile image
Robin Gagnon

Chaining sound super nice! vim-like 🌞

Thread Thread
link2twenty profile image
Andrew Bone Author

If that's a road we wanna go down it might be worth including chaining in the hook currently the only way to do it would be something like.

const [chaining, setChaining] = useState([]);
const [chainingQueue, setChainingQueue] = useState([]);

const pushChaining = useCallback((item)=>{
  setChaining([...chaining, item]);
},[chaining])

useEffect(()=>{ 
  let timeout;

  timeout = window.setTimeout(()=>{
    setChaining([]);
  }, 1500);

  if(!chainingQueue) return;
  pushChaining(chainingQueue);
  setChainingQueue(null)

  return ()=>clearTimeout(timeout);
},[chainingQueue, pushChaining]);

useKeyboardShortcut({
  KeyG: setChainingQueue('KeyG'),
  KeyH: (chaining === ['KeyG'] && function)
})
Enter fullscreen mode Exit fullscreen mode

Which is a bit convoluted to do over and over again.