diff --git a/apps/examples/e2e/shared-e2e.ts b/apps/examples/e2e/shared-e2e.ts index a14f06753..d7755945b 100644 --- a/apps/examples/e2e/shared-e2e.ts +++ b/apps/examples/e2e/shared-e2e.ts @@ -41,15 +41,20 @@ export async function setupPage(page: PlaywrightTestArgs['page']) { } export async function setupPageWithShapes(page: PlaywrightTestArgs['page']) { + // delete everything + await page.keyboard.press('Control+a') + await page.keyboard.press('Backspace') + + // create shapes await page.keyboard.press('r') await page.mouse.click(200, 200) await page.keyboard.press('r') await page.mouse.click(200, 250) await page.keyboard.press('r') await page.mouse.click(250, 300) - await page.evaluate(() => { - editor.selectNone() - }) + + // deselect everything + await page.evaluate(() => editor.selectNone()) } export async function cleanupPage(page: PlaywrightTestArgs['page']) { diff --git a/apps/examples/e2e/tests/test-kbds.spec.ts b/apps/examples/e2e/tests/test-kbds.spec.ts index 391841801..148443637 100644 --- a/apps/examples/e2e/tests/test-kbds.spec.ts +++ b/apps/examples/e2e/tests/test-kbds.spec.ts @@ -64,246 +64,273 @@ test.describe('Keyboard Shortcuts', () => { }) } }) +}) - test.describe('actions', () => { - test('Zoom in', async () => { - await page.keyboard.press('Control+=') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'zoom-in', - data: { source: 'kbd' }, - }) +test.describe('Keyboard Shortcuts', () => { + test.beforeAll(async ({ browser }) => { + page = await browser.newPage() + await setupPage(page) + + // Make some shapes + await page.keyboard.press('r') + await page.mouse.click(100, 100) + await page.keyboard.press('r') + await page.mouse.click(250, 250) + await page.keyboard.press('v') + }) + + test('Zoom in', async () => { + await page.keyboard.press('Control+=') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'zoom-in', + data: { source: 'kbd' }, + }) + }) + + test('Zoom out', async () => { + await page.keyboard.press('Control+-') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'zoom-out', + data: { source: 'kbd' }, + }) + }) + + test('Zoom to fit', async () => { + await page.keyboard.press('Shift+1') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'zoom-to-fit', + data: { source: 'kbd' }, + }) + }) + + test('Zoom to selection', async () => { + await page.keyboard.press('Shift+2') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'zoom-to-selection', + data: { source: 'kbd' }, + }) + }) + + test('Zoom to 100', async () => { + await page.keyboard.press('Shift+0') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'reset-zoom', + data: { source: 'kbd' }, + }) + }) + + /* ---------------------- Files --------------------- */ + + // new-project — Cmd+N + // open — Cmd+O + // save — Cmd+S + // save-as — Cmd+Shift+S + // upload-media — Cmd+I + + /* -------------------- Clipboard ------------------- */ + + // await page.keyboard.press('Control+c') + // expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + // name: 'copy', + // data: { source: 'kbd' }, + // }) + + // await page.keyboard.press('Control+v') + // expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + // name: 'paste', + // data: { source: 'kbd' }, + // }) + + // await page.keyboard.press('Control+x') + // expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + // name: 'cut', + // data: { source: 'kbd' }, + // }) + + test('Toggle grid mode', async () => { + await page.keyboard.press("Control+'") + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'toggle-grid-mode', + data: { source: 'kbd' }, + }) + }) + + test('Toggle dark mode', async () => { + await page.keyboard.press('Control+/') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'toggle-dark-mode', + data: { source: 'kbd' }, + }) + }) + + test('Toggle tool lock', async () => { + await page.keyboard.press('q') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'toggle-tool-lock', + data: { source: 'kbd' }, + }) + }) +}) + +test.describe('Actions on shapes', () => { + test.beforeAll(async ({ browser }) => { + page = await browser.newPage() + await setupPage(page) + }) + + /* -------------- Operations on Shapes -------------- */ + + test('Operations on shapes', async () => { + await setupPageWithShapes(page) + + // select-all — Cmd+A + await page.keyboard.press('Control+a') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'select-all-shapes', + data: { source: 'kbd' }, }) - test('Zoom out', async () => { - await page.keyboard.press('Control+-') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'zoom-out', - data: { source: 'kbd' }, - }) + // flip-h — Shift+H + await page.keyboard.press('Shift+h') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'flip-shapes', + data: { operation: 'horizontal', source: 'kbd' }, }) - test('Zoom to fit', async () => { - await page.keyboard.press('Shift+1') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'zoom-to-fit', - data: { source: 'kbd' }, - }) + // flip-v — Shift+V + await page.keyboard.press('Shift+v') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'flip-shapes', + data: { operation: 'vertical', source: 'kbd' }, }) - test('Zoom to selection', async () => { - await page.keyboard.press('Shift+2') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'zoom-to-selection', - data: { source: 'kbd' }, - }) + // move-to-front — ] + await page.keyboard.press(']') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'reorder-shapes', + data: { operation: 'toFront', source: 'kbd' }, }) - test('Zoom to 100', async () => { - await page.keyboard.press('Shift+0') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'reset-zoom', - data: { source: 'kbd' }, - }) + // move-forward — Alt+] + await page.keyboard.press('Alt+]') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'reorder-shapes', + data: { operation: 'forward', source: 'kbd' }, }) - /* ---------------------- Files --------------------- */ + // move-to-back — [ + await page.keyboard.press('[') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'reorder-shapes', + data: { operation: 'toBack', source: 'kbd' }, + }) - // new-project — Cmd+N - // open — Cmd+O - // save — Cmd+S - // save-as — Cmd+Shift+S - // upload-media — Cmd+I + // move-backward — Alt+[ + await page.keyboard.press('Alt+[') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'reorder-shapes', + data: { operation: 'backward', source: 'kbd' }, + }) - /* -------------------- Clipboard ------------------- */ + // group — Cmd+G + await page.keyboard.press('Control+g') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'group-shapes', + data: { source: 'kbd' }, + }) - // await page.keyboard.press('Control+c') + // ungroup — Cmd+Shift+G + await page.keyboard.press('Control+Shift+g') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'ungroup-shapes', + data: { source: 'kbd' }, + }) + + // duplicate — Cmd+D + await page.keyboard.press('Control+d') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'duplicate-shapes', + data: { source: 'kbd' }, + }) + + // align left — Alt+A + await page.keyboard.press('Alt+a') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'align-shapes', + data: { operation: 'left', source: 'kbd' }, + }) + + // align right — Alt+D + await page.keyboard.press('Alt+d') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'align-shapes', + data: { operation: 'right', source: 'kbd' }, + }) + + // align top — Alt+W + await page.keyboard.press('Alt+w') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'align-shapes', + data: { operation: 'top', source: 'kbd' }, + }) + + // align bottom — Alt+W' + await page.keyboard.press('Alt+s') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'align-shapes', + data: { operation: 'bottom', source: 'kbd' }, + }) + + // delete — backspace + await page.keyboard.press('Control+a') // selected + await page.keyboard.press('Backspace') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'delete-shapes', + data: { source: 'kbd' }, + }) + + // delete — ⌫ + + // Make some shapes and select them + await page.keyboard.press('r') + await page.mouse.click(100, 100) + await page.keyboard.press('r') + await page.mouse.click(250, 250) + await page.keyboard.press('v') + await page.keyboard.press('Control+a') + + await page.keyboard.press('Delete') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'delete-shapes', + data: { source: 'kbd' }, + }) + + /* ---------------------- Misc ---------------------- */ + + // toggle lock + await page.keyboard.press('Shift+l') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'toggle-lock', + }) + + // await page.keyboard.press('Control+i') // expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - // name: 'copy', - // data: { source: 'kbd' }, + // name: 'open-menu', + // data: { source: 'dialog' }, // }) - // await page.keyboard.press('Control+v') + // await page.keyboard.press('Control+u') // expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - // name: 'paste', - // data: { source: 'kbd' }, + // name: 'open-menu', + // data: { source: 'dialog' }, // }) - // await page.keyboard.press('Control+x') - // expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - // name: 'cut', - // data: { source: 'kbd' }, - // }) + /* --------------------- Export --------------------- */ - /* ------------------- Preferences ------------------ */ - - test('Toggle grid mode', async () => { - await page.keyboard.press("Control+'") - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'toggle-grid-mode', - data: { source: 'kbd' }, - }) - }) - - test('Toggle dark mode', async () => { - await page.keyboard.press('Control+/') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'toggle-dark-mode', - data: { source: 'kbd' }, - }) - }) - - test('Toggle tool lock', async () => { - await page.keyboard.press('q') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'toggle-tool-lock', - data: { source: 'kbd' }, - }) - }) - - /* -------------- Operations on Shapes -------------- */ - - test('Operations on shapes', async () => { - await setupPageWithShapes(page) - - // select-all — Cmd+A - await page.keyboard.press('Control+a') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'select-all-shapes', - data: { source: 'kbd' }, - }) - - // flip-h — Shift+H - await page.keyboard.press('Shift+h') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'flip-shapes', - data: { operation: 'horizontal', source: 'kbd' }, - }) - - // flip-v — Shift+V - await page.keyboard.press('Shift+v') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'flip-shapes', - data: { operation: 'vertical', source: 'kbd' }, - }) - - // move-to-front — ] - await page.keyboard.press(']') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'reorder-shapes', - data: { operation: 'toFront', source: 'kbd' }, - }) - - // move-forward — Alt+] - await page.keyboard.press('Alt+]') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'reorder-shapes', - data: { operation: 'forward', source: 'kbd' }, - }) - - // move-to-back — [ - await page.keyboard.press('[') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'reorder-shapes', - data: { operation: 'toBack', source: 'kbd' }, - }) - - // move-backward — Alt+[ - await page.keyboard.press('Alt+[') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'reorder-shapes', - data: { operation: 'backward', source: 'kbd' }, - }) - - // group — Cmd+G - await page.keyboard.press('Control+g') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'group-shapes', - data: { source: 'kbd' }, - }) - - // ungroup — Cmd+Shift+G - await page.keyboard.press('Control+Shift+g') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'ungroup-shapes', - data: { source: 'kbd' }, - }) - - // duplicate — Cmd+D - await page.keyboard.press('Control+d') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'duplicate-shapes', - data: { source: 'kbd' }, - }) - - // delete — backspace - await page.keyboard.press('Backspace') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'delete-shapes', - data: { source: 'kbd' }, - }) - - // delete — ⌫ - await page.keyboard.press('Delete') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'delete-shapes', - data: { source: 'kbd' }, - }) - - // align left — Alt+A - await page.keyboard.press('Alt+a') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'align-shapes', - data: { operation: 'left', source: 'kbd' }, - }) - - // align right — Alt+D - await page.keyboard.press('Alt+d') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'align-shapes', - data: { operation: 'right', source: 'kbd' }, - }) - - // align top — Alt+W - await page.keyboard.press('Alt+w') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'align-shapes', - data: { operation: 'top', source: 'kbd' }, - }) - - // align bottom — Alt+W' - await page.keyboard.press('Alt+s') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'align-shapes', - data: { operation: 'bottom', source: 'kbd' }, - }) - - /* ---------------------- Misc ---------------------- */ - - // toggle lock - await page.keyboard.press('Shift+l') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'toggle-lock', - }) - - // await page.keyboard.press('Control+i') - // expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - // name: 'open-menu', - // data: { source: 'dialog' }, - // }) - - // await page.keyboard.press('Control+u') - // expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - // name: 'open-menu', - // data: { source: 'dialog' }, - // }) - - /* --------------------- Export --------------------- */ - - await page.keyboard.press('Control+Shift+c') - expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ - name: 'copy-as', - data: { format: 'svg', source: 'kbd' }, - }) + await page.keyboard.press('Control+Shift+c') + expect(await page.evaluate(() => __tldraw_ui_event)).toMatchObject({ + name: 'copy-as', + data: { format: 'svg', source: 'kbd' }, }) }) }) diff --git a/apps/examples/e2e/tests/test-shapes.spec.ts b/apps/examples/e2e/tests/test-shapes.spec.ts index e828bff69..8b50a66e0 100644 --- a/apps/examples/e2e/tests/test-shapes.spec.ts +++ b/apps/examples/e2e/tests/test-shapes.spec.ts @@ -97,6 +97,7 @@ test.describe('Shape Tools', () => { }) test('creates shapes clickable tools', async () => { + await page.keyboard.press('v') await page.keyboard.press('Control+a') await page.keyboard.press('Backspace') expect(await getAllShapeTypes(page)).toEqual([]) @@ -124,6 +125,7 @@ test.describe('Shape Tools', () => { // Reset for next time await page.mouse.click(0, 0) // to ensure we're not focused + await page.keyboard.press('v') // go to the select tool await page.keyboard.press('Control+a') await page.keyboard.press('Backspace') } @@ -162,6 +164,7 @@ test.describe('Shape Tools', () => { // Reset for next time await page.mouse.click(0, 0) // to ensure we're not focused + await page.keyboard.press('v') await page.keyboard.press('Control+a') await page.keyboard.press('Backspace') } diff --git a/packages/tldraw/src/lib/ui/components/DuplicateButton.tsx b/packages/tldraw/src/lib/ui/components/DuplicateButton.tsx index 9bbddce99..36334734e 100644 --- a/packages/tldraw/src/lib/ui/components/DuplicateButton.tsx +++ b/packages/tldraw/src/lib/ui/components/DuplicateButton.tsx @@ -10,13 +10,11 @@ export const DuplicateButton = track(function DuplicateButton() { const msg = useTranslation() const action = actions['duplicate'] - const noSelected = editor.selectedShapeIds.length <= 0 - return (