Adding a single E2E test per menu (#2954)

A basic test for each of the menu areas

fixes TLD-2251

- [x] `tests` — Changes to any test code only[^2]

### Release Notes

- Add a brief release note for your PR here.

---------

Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
Taha 2024-02-29 13:21:10 +00:00 committed by GitHub
parent c0d849e8b5
commit 5db4e9a491
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 395 additions and 20 deletions

View file

@ -1,10 +1,20 @@
import { test as base } from '@playwright/test'
import { ActionsMenu } from './menus/ActionsMenu'
import { HelpMenu } from './menus/HelpMenu'
import { MainMenu } from './menus/MainMenu'
import { NavigationPanel } from './menus/NavigationPanel'
import { PageMenu } from './menus/PageMenu'
import { StylePanel } from './menus/StylePanel'
import { Toolbar } from './menus/Toolbar'
type Fixtures = {
toolbar: Toolbar
stylePanel: StylePanel
actionsMenu: ActionsMenu
helpMenu: HelpMenu
mainMenu: MainMenu
pageMenu: PageMenu
navigationPanel: NavigationPanel
}
const test = base.extend<Fixtures>({
@ -16,6 +26,26 @@ const test = base.extend<Fixtures>({
const stylePanel = new StylePanel(page)
await use(stylePanel)
},
actionsMenu: async ({ page }, use) => {
const actionsMenu = new ActionsMenu(page)
await use(actionsMenu)
},
helpMenu: async ({ page }, use) => {
const helpMenu = new HelpMenu(page)
await use(helpMenu)
},
mainMenu: async ({ page }, use) => {
const mainMenu = new MainMenu(page)
await use(mainMenu)
},
pageMenu: async ({ page }, use) => {
const pagemenu = new PageMenu(page)
await use(pagemenu)
},
navigationPanel: async ({ page }, use) => {
const navigationPanel = new NavigationPanel(page)
await use(navigationPanel)
},
})
export default test

View file

@ -0,0 +1,42 @@
import { Locator, Page } from '@playwright/test'
export class ActionsMenu {
readonly quickActions: { [key: string]: Locator }
readonly mainActions: { [key: string]: Locator }
readonly actionsMenuButton: Locator
readonly actionsMenuContent: Locator
constructor(public readonly page: Page) {
this.page = page
this.actionsMenuButton = this.page.getByTestId('actions-menu.button')
this.actionsMenuContent = this.page.getByTestId('actions-menu.content')
this.quickActions = {
undo: this.page.getByTestId('quick-actions.undo'),
redo: this.page.getByTestId('quick-actions.redo'),
delete: this.page.getByTestId('quick-actions.delete'),
duplicate: this.page.getByTestId('quick-actions.duplicate'),
}
this.mainActions = {
alignLeft: this.page.getByTestId('actions-menu.align-left'),
alignCenterHorizontal: this.page.getByTestId('actions-menu.align-center-horizontal'),
alignRight: this.page.getByTestId('actions-menu.align-right'),
stretchHorizontal: this.page.getByTestId('actions-menu.stretch-horizontal'),
alignTop: this.page.getByTestId('actions-menu.align-top'),
alignCenterVertical: this.page.getByTestId('actions-menu.align-center-vertical'),
alignBottom: this.page.getByTestId('actions-menu.align-bottom'),
stretchVertical: this.page.getByTestId('actions-menu.stretch-vertical'),
distributeHorizontal: this.page.getByTestId('actions-menu.distribute-horizontal'),
distributeVertical: this.page.getByTestId('actions-menu.distribute-vertical'),
stackHorizontal: this.page.getByTestId('actions-menu.stack-horizontal'),
stackVertical: this.page.getByTestId('actions-menu.stack-vertical'),
sendToBack: this.page.getByTestId('actions-menu.send-to-back'),
sendBackward: this.page.getByTestId('actions-menu.send-backward'),
bringForward: this.page.getByTestId('actions-menu.bring-forward'),
bringToFront: this.page.getByTestId('actions-menu.bring-to-front'),
rotateCCW: this.page.getByTestId('actions-menu.rotate-ccw'),
rotateCW: this.page.getByTestId('actions-menu.rotate-cw'),
editLink: this.page.getByTestId('actions-menu.edit-link'),
group: this.page.getByTestId('actions-menu.group'),
}
}
}

View file

@ -0,0 +1,34 @@
import { Locator, Page } from '@playwright/test'
export class HelpMenu {
readonly helpMenuButton: Locator
readonly languagesButton: Locator
readonly languagesContent: Locator
readonly keyboardShortcutsMenu: KeyboardShortcutsMenu
readonly linkGroup: { [key: string]: Locator }
constructor(public readonly page: Page) {
this.page = page
this.helpMenuButton = this.page.getByTestId('help-menu.button')
this.languagesButton = this.page.getByTestId('help-menu-sub.language-button')
this.languagesContent = this.page.getByTestId('help-menu-sub.language-content')
this.keyboardShortcutsMenu = new KeyboardShortcutsMenu(this.page)
this.linkGroup = {
github: this.page.getByTestId('help-menu.github'),
twitter: this.page.getByTestId('help-menu.twitter'),
discord: this.page.getByTestId('help-menu.discord'),
about: this.page.getByTestId('help-menu.about'),
}
}
}
class KeyboardShortcutsMenu {
readonly heading: Locator
readonly button: Locator
readonly closeButton: Locator
constructor(private readonly page: Page) {
this.heading = this.page.getByRole('dialog').getByText('Keyboard shortcuts')
this.button = this.page.getByTestId('help-menu.keyboard-shortcuts-button')
this.closeButton = this.page.getByRole('dialog').getByTestId('dialog.close')
}
}

View file

@ -0,0 +1,63 @@
import { Locator, Page } from '@playwright/test'
export class MainMenu {
readonly mainMenuButton: Locator
readonly buttons: { [key: string]: Locator }
readonly subMenus: { [key: string]: Locator[] }
constructor(public readonly page: Page) {
this.page = page
this.mainMenuButton = this.page.getByTestId('main-menu.button')
this.buttons = {
edit: this.page.getByRole('menuitem').getByText('Edit'),
object: this.page.getByRole('menuitem').getByText('Object'),
view: this.page.getByRole('menuitem').getByText('View'),
insertEmbed: this.page.getByRole('menuitem').getByText('Insert Embed'),
uploadMedia: this.page.getByRole('menuitem').getByText('Upload Media'),
preferences: this.page.getByRole('menuitem').getByText('Preferences'),
}
this.subMenus = {
editSubmenu: [
this.page.getByTestId('main-menu-sub.edit-content').getByText('Undo'),
this.page.getByTestId('main-menu-sub.edit-content').getByText('Redo'),
this.page.getByTestId('main-menu-sub.edit-content').getByText('Cut'),
this.page.getByTestId('main-menu-sub.edit-content').getByText('Copy'),
this.page.getByTestId('main-menu-sub.edit-content').getByText('Copy as'),
this.page.getByTestId('main-menu-sub.edit-content').getByText('Duplicate'),
this.page.getByTestId('main-menu-sub.edit-content').getByText('Paste'),
this.page.getByTestId('main-menu-sub.edit-content').getByText('Delete'),
this.page.getByTestId('main-menu-sub.edit-content').getByText('Select all'),
],
objectSubmenu: [
this.page.getByRole('menuitem').getByText('Export as'),
this.page.getByRole('menuitem').getByText('Group'),
this.page.getByRole('menuitem').getByText('Ungroup'),
this.page.getByRole('menuitem').getByText('Remove frame'),
this.page.getByRole('menuitem').getByText('Fit to content'),
this.page.getByRole('menuitem').getByText('Toggle auto size'),
this.page.getByRole('menuitem').getByText('Edit link'),
this.page.getByRole('menuitem').getByText('Convert to Bookmark'),
this.page.getByRole('menuitem').getByText('Convert to Embed'),
this.page.getByRole('menuitem').getByText('Toggle locked'),
this.page.getByRole('menuitem').getByText('Unlock all'),
],
viewSubmenu: [
this.page.getByTestId('main-menu-sub.view-content').getByText('Zoom in'),
this.page.getByTestId('main-menu-sub.view-content').getByText('Zoom out'),
this.page.getByTestId('main-menu-sub.view-content').getByText('Zoom to 100%'),
this.page.getByTestId('main-menu-sub.view-content').getByText('Zoom to fit'),
this.page.getByTestId('main-menu-sub.view-content').getByText('Zoom to selection'),
],
preferencesSubmenu: [
this.page.getByTestId('main-menu-sub.preferences-content').getByText('Always snap'),
this.page.getByTestId('main-menu-sub.preferences-content').getByText('Tool lock'),
this.page.getByTestId('main-menu-sub.preferences-content').getByText('Show grid'),
this.page.getByTestId('main-menu-sub.preferences-content').getByText('Dark mode'),
this.page.getByTestId('main-menu-sub.preferences-content').getByText('Focus mode'),
this.page.getByTestId('main-menu-sub.preferences-content').getByText('Edge scrolling'),
this.page.getByTestId('main-menu-sub.preferences-content').getByText('Reduce motion'),
this.page.getByTestId('main-menu-sub.preferences-content').getByText('Debug mode'),
this.page.getByTestId('main-menu-sub.preferences-content').getByText('Language'),
],
}
}
}

View file

@ -0,0 +1,21 @@
import { Locator, Page } from '@playwright/test'
export class NavigationPanel {
readonly minimap: Locator
readonly zoomMenuButton: Locator
readonly toggleButton: Locator
readonly zoomMenuItems: { [key: string]: Locator }
constructor(private readonly page: Page) {
this.page = page
this.minimap = page.getByLabel('minimap')
this.zoomMenuButton = page.getByTestId('minimap.zoom-menu-button')
this.toggleButton = page.getByTestId('minimap.toggle-button')
this.zoomMenuItems = {
zoomIn: page.getByRole('menuitem').getByText('Zoom in'),
zoomOut: page.getByRole('menuitem').getByText('Zoom out'),
zoomToHundred: page.getByRole('menuitem').getByText('Zoom to 100%'),
zoomToFit: page.getByRole('menuitem').getByText('Zoom to fit'),
zoomToSelection: page.getByRole('menuitem').getByText('Zoom to selection'),
}
}
}

View file

@ -0,0 +1,12 @@
import { Locator, Page } from '@playwright/test'
export class PageMenu {
readonly pagemenuButton: Locator
readonly header: Locator
constructor(public readonly page: Page) {
this.page = page
this.pagemenuButton = this.page.getByTestId('page-menu.button')
this.header = this.page.getByRole('dialog').getByText('Pages')
}
}

View file

@ -0,0 +1,20 @@
import { expect } from '@playwright/test'
import { setup } from '../shared-e2e'
import test from './fixtures/fixtures'
test.describe('actions menu', () => {
test.beforeEach(setup)
test('you can open and close the actions menu', async ({ actionsMenu }) => {
const { actionsMenuButton, actionsMenuContent } = actionsMenu
await expect(actionsMenuContent).toBeHidden()
await actionsMenuButton.click()
await expect(actionsMenuContent).toBeVisible()
await actionsMenuButton.click()
await expect(actionsMenuContent).toBeHidden()
})
// ...
// More tests here
// ...
})

View file

@ -45,11 +45,11 @@ test.describe.skip('clipboard tests', () => {
expect(await page.evaluate(() => editor.getCurrentPageShapes().length)).toBe(1)
expect(await page.evaluate(() => editor.getSelectedShapes().length)).toBe(1)
await page.getByTestId('main.menu').click()
await page.getByTestId('main-menu.button').click()
await page.getByTestId('main-menu-sub-trigger.edit').click()
await page.getByTestId('main-menu.copy').click()
await sleep(100)
await page.getByTestId('main.menu').click()
await page.getByTestId('main-menu.button').click()
await page.getByTestId('main-menu-sub-trigger.edit').click()
await page.getByTestId('main-menu.paste').click()

View file

@ -0,0 +1,41 @@
import { expect } from '@playwright/test'
import { setup } from '../shared-e2e'
import test from './fixtures/fixtures'
test.describe('help menu', () => {
test.beforeEach(setup)
test('you can open and close the menus', async ({ helpMenu, isMobile }) => {
// No help menu on mobile
const { helpMenuButton, languagesButton, keyboardShortcutsMenu, languagesContent } = helpMenu
test.skip(isMobile, 'only run on desktop')
await test.step('open help menu', async () => {
await expect(languagesButton).toBeHidden()
await expect(keyboardShortcutsMenu.button).toBeHidden()
await helpMenuButton.click()
await expect(languagesButton).toBeVisible()
await expect(keyboardShortcutsMenu.button).toBeVisible()
})
await test.step('hover languages submenu', async () => {
await expect(languagesContent).toBeHidden()
await languagesButton.hover()
await expect(languagesContent).toBeVisible()
})
await test.step('open the keyboard shortcuts menu', async () => {
await expect(keyboardShortcutsMenu.heading).toBeHidden()
await keyboardShortcutsMenu.button.click()
await expect(keyboardShortcutsMenu.heading).toBeVisible()
})
await test.step('close the keyboard shortcuts menu', async () => {
await keyboardShortcutsMenu.closeButton.click()
await expect(keyboardShortcutsMenu.heading).toBeHidden()
await expect(languagesButton).toBeHidden()
})
})
// ...
// More tests here
// ...
})

View file

@ -0,0 +1,44 @@
import { expect } from '@playwright/test'
import { setup } from '../shared-e2e'
import test from './fixtures/fixtures'
test.describe('help menu', () => {
test.beforeEach(setup)
test('you can open and close the menu', async ({ mainMenu, page }) => {
const { mainMenuButton, buttons } = mainMenu
const { editSubmenu, viewSubmenu, preferencesSubmenu } = mainMenu.subMenus
const submenus = [
{ name: 'preferences', submenu: preferencesSubmenu, button: buttons.preferences },
{ name: 'view', submenu: viewSubmenu, button: buttons.view },
{ name: 'edit', submenu: editSubmenu, button: buttons.edit },
]
await test.step('open main menu', async () => {
await expect(buttons.edit).toBeHidden()
await mainMenuButton.click()
await expect(buttons.edit).toBeVisible()
})
for (const submenu of submenus) {
await test.step(`hovering opens ${submenu.name} submenu`, async () => {
await expect(submenu.submenu[0]).toBeHidden()
await submenu.button.hover()
await expect(submenu.submenu[0]).toBeVisible()
})
}
await test.step('close main menu', async () => {
// close the menu by clicking the main menu button again
await mainMenuButton.click()
await expect(buttons.edit).toBeHidden()
// open the menu again
await mainMenuButton.click()
// click somewhere on the canvas to close the menu
await page.mouse.click(250, 150)
await expect(buttons.edit).toBeHidden()
})
})
// ...
// More tests here
// ...
})

View file

@ -0,0 +1,33 @@
import { expect } from '@playwright/test'
import { setup } from '../shared-e2e'
import test from './fixtures/fixtures'
test.describe('navigationPanel', () => {
test.beforeEach(setup)
test('you can open and close the zoom menu', async ({ navigationPanel, isMobile }) => {
// no navigationPanel on mobile
test.skip(isMobile)
const { zoomMenuButton } = navigationPanel
const { zoomIn } = navigationPanel.zoomMenuItems
await expect(zoomIn).toBeHidden()
await zoomMenuButton.click()
await expect(zoomIn).toBeVisible()
await zoomMenuButton.click()
await expect(zoomIn).toBeHidden()
})
test('you can toggle the minimap', async ({ navigationPanel, isMobile }) => {
// no navigationPanel on mobile
test.skip(isMobile)
const { minimap, toggleButton } = navigationPanel
await expect(minimap).toBeHidden()
await toggleButton.click()
await expect(minimap).toBeVisible()
await toggleButton.click()
await expect(minimap).toBeHidden()
})
// ...
// More tests here
// ...
})

View file

@ -0,0 +1,20 @@
import { expect } from '@playwright/test'
import { setup } from '../shared-e2e'
import test from './fixtures/fixtures'
test.describe('page menu', () => {
test.beforeEach(setup)
test('you can open and close the page menu', async ({ pageMenu }) => {
const { pagemenuButton, header } = pageMenu
await expect(header).toBeHidden()
await pagemenuButton.click()
await expect(header).toBeVisible()
await pagemenuButton.click()
await expect(header).toBeHidden()
})
// ...
// More tests here
// ...
})

View file

@ -1479,7 +1479,7 @@ export function TldrawUiDropdownMenuRoot({ id, children, modal, debugOpen, }: TL
export function TldrawUiDropdownMenuSub({ id, children }: TLUiDropdownMenuSubProps): JSX_2.Element;
// @public (undocumented)
export function TldrawUiDropdownMenuSubTrigger({ label, title, disabled, }: TLUiDropdownMenuSubTriggerProps): JSX_2.Element;
export function TldrawUiDropdownMenuSubTrigger({ id, label, title, disabled, }: TLUiDropdownMenuSubTriggerProps): JSX_2.Element;
// @public (undocumented)
export function TldrawUiDropdownMenuTrigger({ children, ...rest }: TLUiDropdownMenuTriggerProps): JSX_2.Element;
@ -1910,7 +1910,7 @@ export interface TLUiEventMap {
}
// @public (undocumented)
export type TLUiEventSource = 'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'document-name' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'style-panel' | 'toolbar' | 'unknown' | 'zoom-menu';
export type TLUiEventSource = 'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'document-name' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'main-menu' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'style-panel' | 'toolbar' | 'unknown' | 'zoom-menu';
// @public (undocumented)
export type TLUiHelperButtonsProps = {

View file

@ -17517,7 +17517,7 @@
"excerptTokens": [
{
"kind": "Content",
"text": "export declare function TldrawUiDropdownMenuSubTrigger({ label, title, disabled, }: "
"text": "export declare function TldrawUiDropdownMenuSubTrigger({ id, label, title, disabled, }: "
},
{
"kind": "Reference",
@ -17551,7 +17551,7 @@
"overloadIndex": 1,
"parameters": [
{
"parameterName": "{ label, title, disabled, }",
"parameterName": "{ id, label, title, disabled, }",
"parameterTypeTokenRange": {
"startIndex": 1,
"endIndex": 2
@ -22024,7 +22024,7 @@
},
{
"kind": "Content",
"text": "'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'document-name' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'style-panel' | 'toolbar' | 'unknown' | 'zoom-menu'"
"text": "'actions-menu' | 'context-menu' | 'debug-panel' | 'dialog' | 'document-name' | 'export-menu' | 'help-menu' | 'helper-buttons' | 'kbd' | 'main-menu' | 'menu' | 'navigation-zone' | 'page-menu' | 'people-menu' | 'quick-actions' | 'share-menu' | 'style-panel' | 'toolbar' | 'unknown' | 'zoom-menu'"
},
{
"kind": "Content",

View file

@ -47,7 +47,7 @@ export const DefaultActionsMenu = memo(function DefaultActionsMenu({
<TldrawUiPopoverTrigger>
<TldrawUiButton
type="icon"
data-testid="main.action-menu"
data-testid="actions-menu.button"
title={msg('actions-menu.title')}
>
<TldrawUiButtonIcon icon="dots-vertical" small />
@ -57,7 +57,7 @@ export const DefaultActionsMenu = memo(function DefaultActionsMenu({
side={breakpoint >= PORTRAIT_BREAKPOINT.TABLET ? 'bottom' : 'top'}
sideOffset={6}
>
<div className="tlui-actions-menu tlui-buttons__grid">
<div className="tlui-actions-menu tlui-buttons__grid" data-testid="actions-menu.content">
<TldrawUiMenuContextProvider type="icons" sourceId="actions-menu">
{content}
</TldrawUiMenuContextProvider>

View file

@ -33,7 +33,7 @@ export const DefaultHelpMenu = memo(function DefaultHelpMenu({ children }: TLUiH
<div className="tlui-help-menu">
<TldrawUiDropdownMenuRoot id="help menu">
<TldrawUiDropdownMenuTrigger>
<TldrawUiButton type="help" title={msg('help-menu.title')}>
<TldrawUiButton type="help" title={msg('help-menu.title')} data-testid="help-menu.button">
<TldrawUiButtonIcon icon="question-mark" small />
</TldrawUiButton>
</TldrawUiDropdownMenuTrigger>

View file

@ -21,7 +21,7 @@ export function KeyboardShortcutsMenuItem() {
return (
<TldrawUiMenuItem
id="keyboard-shortcuts"
id="keyboard-shortcuts-button"
label="help-menu.keyboard-shortcuts"
readonlyOk
onSelect={() => {

View file

@ -27,7 +27,7 @@ export const DefaultMainMenu = memo(function DefaultMainMenu({ children }: TLUiM
return (
<_Dropdown.Root dir="ltr" open={isOpen} onOpenChange={onOpenChange} modal={false}>
<_Dropdown.Trigger asChild dir="ltr">
<TldrawUiButton type="icon" data-testid="main.menu" title={msg('menu.title')}>
<TldrawUiButton type="icon" data-testid="main-menu.button" title={msg('menu.title')}>
<TldrawUiButtonIcon icon="menu" small />
</TldrawUiButton>
</_Dropdown.Trigger>
@ -40,7 +40,7 @@ export const DefaultMainMenu = memo(function DefaultMainMenu({ children }: TLUiM
alignOffset={0}
sideOffset={6}
>
<TldrawUiMenuContextProvider type="menu" sourceId="menu">
<TldrawUiMenuContextProvider type="menu" sourceId="main-menu">
{content}
</TldrawUiMenuContextProvider>
</_Dropdown.Content>

View file

@ -221,6 +221,8 @@ export function DefaultMinimap() {
return (
<div className="tlui-minimap">
<canvas
role="img"
aria-label="minimap"
ref={rCanvas}
className="tlui-minimap__canvas"
onDoubleClick={onDoubleClick}

View file

@ -38,7 +38,7 @@ export const DefaultNavigationPanel = memo(function DefaultNavigationPanel() {
{Minimap && (
<TldrawUiButton
type="icon"
data-testid="minimap.toggle"
data-testid="minimap.toggle-button"
title={msg('navigation-zone.toggle-minimap')}
className="tlui-navigation-panel__toggle"
onClick={toggleMinimap}
@ -69,7 +69,7 @@ export const DefaultNavigationPanel = memo(function DefaultNavigationPanel() {
{Minimap && (
<TldrawUiButton
type="icon"
data-testid="minimap.toggle"
data-testid="minimap.toggle-button"
title={msg('navigation-zone.toggle-minimap')}
className="tlui-navigation-panel__toggle"
onClick={toggleMinimap}

View file

@ -263,7 +263,12 @@ export const DefaultPageMenu = memo(function DefaultPageMenu() {
return (
<TldrawUiPopover id="pages" onOpenChange={onOpenChange} open={isOpen}>
<TldrawUiPopoverTrigger data-testid="main.page-menu">
<TldrawUiButton type="menu" title={currentPage.name} className="tlui-page-menu__trigger">
<TldrawUiButton
type="menu"
title={currentPage.name}
data-testid="page-menu.button"
className="tlui-page-menu__trigger"
>
<div className="tlui-page-menu__name">{currentPage.name}</div>
<TldrawUiButtonIcon icon="chevron-down" small />
</TldrawUiButton>

View file

@ -64,7 +64,7 @@ const ZoomTriggerButton = forwardRef<HTMLButtonElement, any>(
{...props}
type="icon"
title={`${msg('navigation-zone.zoom')}`}
data-testid="minimap.zoom-menu"
data-testid="minimap.zoom-menu-button"
className={
breakpoint < PORTRAIT_BREAKPOINT.TABLET_SM
? 'tlui-zoom-menu__button'

View file

@ -115,6 +115,7 @@ export type TLUiDropdownMenuSubTriggerProps = {
/** @public */
export function TldrawUiDropdownMenuSubTrigger({
id,
label,
title,
disabled,
@ -122,6 +123,7 @@ export function TldrawUiDropdownMenuSubTrigger({
return (
<_DropdownMenu.SubTrigger dir="ltr" asChild disabled={disabled}>
<TldrawUiButton
data-testid={id}
type="menu"
className="tlui-menu__submenu__trigger"
disabled={disabled}
@ -145,6 +147,7 @@ export type TLUiDropdownMenuSubContentProps = {
/** @public */
export function TldrawUiDropdownMenuSubContent({
id,
alignOffset = -1,
sideOffset = -4,
size = 'small',
@ -154,6 +157,7 @@ export function TldrawUiDropdownMenuSubContent({
return (
<_DropdownMenu.Portal container={container}>
<_DropdownMenu.SubContent
data-testid={id}
className="tlui-menu tlui-menu__submenu__content"
alignOffset={alignOffset}
sideOffset={sideOffset}

View file

@ -14,7 +14,7 @@ export type TldrawUiMenuContextType =
const menuContext = createContext<{
type: TldrawUiMenuContextType
sourceId: TLUiEventSource
}>({ type: 'menu', sourceId: 'menu' })
}>({ type: 'menu', sourceId: 'main-menu' })
/** @public */
export function useTldrawUiMenuContext() {

View file

@ -50,12 +50,15 @@ export function TldrawUiMenuSubmenu<Translation extends string = string>({
return (
<TldrawUiDropdownMenuSub id={`${sourceId}-sub.${id}`}>
<TldrawUiDropdownMenuSubTrigger
id={`${sourceId}-sub.${id}`}
id={`${sourceId}-sub.${labelStr ? labelStr.toLowerCase() + '-button' : ''}`}
disabled={disabled}
label={labelStr!}
title={labelStr!}
/>
<TldrawUiDropdownMenuSubContent id={`${sourceId}-sub-content.${id}`} size={size}>
<TldrawUiDropdownMenuSubContent
id={`${sourceId}-sub.${labelStr ? labelStr.toLowerCase() + '-content' : ''}`}
size={size}
>
{children}
</TldrawUiDropdownMenuSubContent>
</TldrawUiDropdownMenuSub>

View file

@ -3,6 +3,7 @@ import * as React from 'react'
/** @public */
export type TLUiEventSource =
| 'menu'
| 'main-menu'
| 'context-menu'
| 'zoom-menu'
| 'document-name'