Skip to content

Conversation

@ThusharaJ07
Copy link
Contributor

PR has below:
1.) new component in Labs for KeyboardKey
2.)Keyboard shortcut stories has below:

  • react-hotkeys-hooks example on how to ass shortcuts
  • keyboard panel to assign shortcuts

@changeset-bot
Copy link

changeset-bot bot commented Dec 8, 2025

🦋 Changeset detected

Latest commit: 7b953e0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@salt-ds/lab Minor
@salt-ds/data-grid Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Dec 8, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
saltdesignsystem Ready Ready Preview, Comment Dec 24, 2025 7:41am

"@salt-ds/lab": minor
---

KS pattern - Added KS pattern and KeyboardKey component.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Treat changelogs as documentation, does every Salt user know what KS is ?
So I would say Keyboard Shortcuts rather than KS and describe what the pattern is, so when I read the changelog, I would say, "I know what that is".

title: "Patterns/Keyboard Shortcuts",
} as Meta;

// Keyboard shortcut data type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT this comment adds no value, as the type KeyboardShortcut offers the same description, so we could remove.

// Keyboard shortcut data type
type KeyboardShortcut = {
label: string;
shortcut: string[][];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding a comment here may make sense as it's no immediate obvious why it's an array of arrays.

label: "Open command palette",
shortcut: [["meta", "option", "p"]],
connector: "+",
action: () => alert("Open command palette triggered!"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT remove exclamation marks

import { KeyboardKey, Table, TBody, TD, TH, THead, TR } from "@salt-ds/lab";
import type { Meta } from "@storybook/react-vite";
import React, { type ChangeEvent, type SyntheticEvent, useState } from "react";
import { HotkeysProvider, useHotkeys } from "react-hotkeys-hook";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are there any types we can use from there ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found two types. Not many

},
];

// Utility: highlight search match
Copy link
Contributor

@mark-tate mark-tate Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function will be used in code, where this comment does not exist.
So when I see a comment on a function, I often ask myself, can I improve the naming and remove the comment.

In this case, I am not sure we need a comment, the naming makes sense.


return (
<kbd ref={ref} className={clsx(withBaseName(), className, {})} {...props}>
<Text>{children}</Text>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to use here we can just put the styling on the root element via CSS

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont use the text element and add the style to the kbd instead

>
{comboArr.map((keyName, idx) => (
<React.Fragment key={keyName + idx}>
<KeyboardKey aria-label={displayKeyName(keyName)}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is aria-label needed if the text node is the same ?

<FlexLayout align="center" gap={1}>
<Text>hit </Text>
<FlexLayout align="center" gap={0}>
<KeyboardKey>ctrl</KeyboardKey>+<KeyboardKey>shift</KeyboardKey>+
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ctrl isn't the right key, you've set it to meta

// Register all hotkeys when enabled
const RegisterShortcuts: React.FC<{ enabled: boolean }> = ({ enabled }) => {
keyboardShortcuts.forEach((shortcut) => {
shortcut.shortcut.forEach((comboArr) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hook violation
We want to have examples as simple copy and paste or to be read and understood, without layers of abstractions that hide what is happening.

I would accept some duplication in stories if it creates better clarity.

Actions
</Text>
<StackLayout gap={filteredShortcuts.length ? 3 : 0.75}>
<ComboBox
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just be an input?

Actions
</Text>
<StackLayout gap={filteredShortcuts.length ? 3 : 0.75}>
<ComboBox
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This control doesn't have an accessible label

s.label.toLowerCase().includes(filter.trim().toLowerCase()),
);

// Handlers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lo-value comment

@@ -0,0 +1,16 @@
.saltKeyboardKey {
display: flex;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be inline-flex


import keyboardKeyCss from "./KeyboardKey.css";

export interface KeyboardKeyProps extends HTMLAttributes<HTMLElement> {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export interface KeyboardKeyProps extends HTMLAttributes<HTMLElement> {}
export interface KeyboardKeyProps extends ComponentPropsWithoutRef<"kbd"> {}

{/* Register all shortcuts, only when enabled */}
<RegisterShortcuts enabled={shortcutsEnabled} />
<StackLayout gap={1}>
<Button data-testid="dialog-button" onClick={handleDialogOpen}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this toggle open/close?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the dialog button is not visible when the dialog is open. Visually not able to toggle it.

});

return (
<kbd ref={ref} className={clsx(withBaseName(), className, {})} {...props}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<kbd ref={ref} className={clsx(withBaseName(), className, {})} {...props}>
<kbd ref={ref} className={clsx(withBaseName(), className)} {...props}>

<Text className="keyboardShortcuts-actions-title" styleAs="h3">
Actions
</Text>
<StackLayout gap={filteredShortcuts.length ? 3 : 0.75}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gap = 0.75?


const withBaseName = makePrefixer("saltKeyboardKey");

export const KeyboardKey = forwardRef<HTMLDivElement, KeyboardKeyProps>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This the see called KBD, like we name Table elements ?

Copy link
Contributor Author

@ThusharaJ07 ThusharaJ07 Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have renamed it to Kbd following the Pascal case in React. Let me know if it make sense to be KBD.


return (
<HotkeysProvider>
{/* Register all shortcuts, only when enabled */}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should align to the docs..
Disabled shortcuts are done this way
https://react-hotkeys-hook.vercel.app/docs/documentation/useHotkeys/disable-hotkeys

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@joshwooding joshwooding marked this pull request as draft December 8, 2025 16:18
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enter a non-match into the filter and the layout collapses.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@honey-chang should I add some min-height to maintain the height of the dialog when search has no match?

if (key === "meta") return "ctrl";
if (key === "option") return "option";
if (key === "shift") return "shift";
return key;
Copy link
Contributor

@mark-tate mark-tate Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe

return function displayKeyName(key: string): string {
  const isMac = navigator.platform.toUpperCase().includes('MAC');
  
  const keyMap: Record<string, string> = {
    meta: isMac ? "⌘" : "ctrl",
    option: isMac ? "⌥" : "alt",
    shift: isMac ? "⇧" : "shift",
  };
  
  return keyMap[key] ?? key;
}

Keyboard shortcuts panel
</Button>
<FlexLayout align="center" gap={1} wrap>
<Text>hit </Text>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could it just be press ?

Term Usage Why
Press ✅ Standard Official term in WCAG, ARIA, and UI guidelines
Hit ❌ Informal Sounds aggressive, not professional

@@ -0,0 +1,220 @@
---
title: Keyboard Shortcuts
description: "Keyboard shortcuts allow users to execute actions, using a keyboard, that are usually performed with a mouse or track pad. They increase the efficiency and productivity of users who primarily rely on their keyboard when interacting with applications."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trackpad is one word ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

]
components: ["Avatar", "Button", "Flow layout", "Icon", "Stack layout", "Text", "Tooltip"]
---
The keyboard shortcuts displays a comprehensive, searchable list of all available shortcuts, helping users quickly learn and reference key combinations to navigate and perform actions efficiently.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all available has some redundancy, can just be available.


Use this pattern to:
- Increase task efficiency by enabling users to perform actions quickly.
- Provide a true alternative to mouse or track pad navigation, supporting users who prefer or require keyboard input.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trackpad

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spell check in site was throwing error for trackpad as a single work. let me check again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

- Increase task efficiency by enabling users to perform actions quickly.
- Provide a true alternative to mouse or track pad navigation, supporting users who prefer or require keyboard input.
- Clearly communicate available shortcuts to help users discover and learn efficient workflows.
- Allow users to search for and execute actions directly from the keyboard, improving accessibility and user experience.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does not sound right... you can search and you can execute but you do not execute an action from the search. Maybe search needs a clearer description ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@honey-chang can you help with the same? I search, we only search for a shortcut. the execution happens separately.


### Anatomy

1. **Keyboard shortcuts switch**: A switch that allows users to turn the keyboard shortcuts list on or off.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it on or off or is it enable/disable... In our language we have disabled components, not on or off components.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@honey-chang , should i change it list enabled/ disabled?

1. **Keyboard shortcuts switch**: A switch that allows users to turn the keyboard shortcuts list on or off.
2. **Filter**: This allows users to quickly search and locate specific shortcuts by typing keywords.
3. **Action name**: Actions represent user-initiated commands—such as Save or Delete—that correspond to defined shortcuts. Each action should be clearly named to reflect the UI command executed by the shortcut.
4. **Description**: For actions that are unique to your application or require additional context, a brief description can be provided to help users understand their purpose.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed we do not match on this, in the search, is that not recommended ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Mark, currently the search matches only the shortcut name (like we have in UI toolkit). Let me know if I should add something here.
~I am unsure what the comment means.

2. **Filter**: This allows users to quickly search and locate specific shortcuts by typing keywords.
3. **Action name**: Actions represent user-initiated commands—such as Save or Delete—that correspond to defined shortcuts. Each action should be clearly named to reflect the UI command executed by the shortcut.
4. **Description**: For actions that are unique to your application or require additional context, a brief description can be provided to help users understand their purpose.
3. **Key**: Each keyboard shortcut consists of two types of keys—modifier keys and character keys—which work together to trigger the action.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds wordy

Option 1: Simpler
Key: Each keyboard shortcut combines modifier keys (like Ctrl or Shift) with character keys to trigger an action.

Option 2: More Concise
Key: Keyboard shortcuts combine modifier keys and character keys to trigger actions.

Option 3: Active Voice
Key: Press modifier keys (such as Ctrl, Shift, or Alt) together with character keys to trigger shortcuts.

Option 4: With Examples
Key: Each shortcut combines modifier keys (Ctrl, Shift, Alt, Cmd) with character keys (letters, numbers, symbols) to perform an action.

Option 5: Most Natural
Key: Keyboard shortcuts use a combination of modifier keys and character keys to perform actions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@honey-chang , should i keep option 4 with examples sounds more informative and has a flow too. thoughts?

alt="Keyboard shortcuts with all available shortcuts."
/>

2. When the user enters text in the filter input, the list updates to show only the matching shortcuts. For more details on filtering behavior, refer to the List Filtering pattern page.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should that be a link to List Filtering pattern

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants