Skip to content

Commit 81880f0

Browse files
authored
Implement document drag and drop3 (#178)
* feat: implement drag and drop * chore: fix scroll to top when insert database at the bottom * chore: fix scroll * chore: lint * fix: test * fix: shift + left rigit selection * chore: add test * chore: fix test * test: drag and drop image * chore: selector * chore: update test with selector * fix: test
1 parent 4dbf18d commit 81880f0

File tree

63 files changed

+2239
-375
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2239
-375
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import { AuthTestUtils } from '../../../support/auth-utils';
2+
import { EditorSelectors, SlashCommandSelectors, waitForReactUpdate } from '../../../support/selectors';
3+
import { generateRandomEmail } from '../../../support/test-config';
4+
5+
describe('Panel Selection - Shift+Arrow Keys', () => {
6+
const authUtils = new AuthTestUtils();
7+
const testEmail = generateRandomEmail();
8+
9+
before(() => {
10+
cy.viewport(1280, 720);
11+
});
12+
13+
beforeEach(() => {
14+
cy.on('uncaught:exception', () => false);
15+
16+
cy.session(testEmail, () => {
17+
authUtils.signInWithTestUrl(testEmail);
18+
}, {
19+
validate: () => {
20+
cy.window().then((win) => {
21+
const token = win.localStorage.getItem('af_auth_token');
22+
expect(token).to.be.ok;
23+
});
24+
}
25+
});
26+
27+
cy.visit('/app');
28+
cy.url({ timeout: 30000 }).should('include', '/app');
29+
cy.contains('Getting started', { timeout: 10000 }).should('be.visible').click();
30+
cy.wait(2000);
31+
32+
EditorSelectors.firstEditor().click({ force: true });
33+
cy.focused().type('{selectall}{backspace}');
34+
waitForReactUpdate(500);
35+
});
36+
37+
describe('Slash Panel Selection', () => {
38+
it('should allow Shift+Arrow selection when slash panel is open', () => {
39+
// Type some text first
40+
cy.focused().type('Hello World');
41+
waitForReactUpdate(200);
42+
43+
// Open slash panel
44+
cy.focused().type('/');
45+
waitForReactUpdate(500);
46+
47+
// Verify slash panel is open
48+
SlashCommandSelectors.slashPanel().should('be.visible');
49+
50+
// Type search text
51+
cy.focused().type('head');
52+
waitForReactUpdate(200);
53+
54+
// Now try Shift+Left to select text - this should work after the fix
55+
cy.focused().type('{shift}{leftArrow}{leftArrow}{leftArrow}{leftArrow}');
56+
waitForReactUpdate(200);
57+
58+
// The selection should have happened - verify by typing replacement text
59+
// Close panel first
60+
cy.focused().type('{esc}');
61+
waitForReactUpdate(200);
62+
63+
// The text "head" should still be visible (since we selected but didn't delete)
64+
EditorSelectors.slateEditor().should('contain.text', 'head');
65+
});
66+
67+
it('should allow Shift+Right selection when slash panel is open', () => {
68+
// Type some text first
69+
cy.focused().type('Test Content');
70+
waitForReactUpdate(200);
71+
72+
// Move cursor to after "Test "
73+
cy.focused().type('{home}');
74+
cy.focused().type('{rightArrow}{rightArrow}{rightArrow}{rightArrow}{rightArrow}');
75+
waitForReactUpdate(200);
76+
77+
// Open slash panel
78+
cy.focused().type('/');
79+
waitForReactUpdate(500);
80+
81+
// Verify slash panel is open
82+
SlashCommandSelectors.slashPanel().should('be.visible');
83+
84+
// Type search text
85+
cy.focused().type('para');
86+
waitForReactUpdate(200);
87+
88+
// Try Shift+Right to extend selection
89+
cy.focused().type('{shift}{rightArrow}{rightArrow}');
90+
waitForReactUpdate(200);
91+
92+
// Close panel
93+
cy.focused().type('{esc}');
94+
waitForReactUpdate(200);
95+
96+
// Verify editor still has content
97+
EditorSelectors.slateEditor().should('contain.text', 'Test');
98+
});
99+
100+
it('should still block plain Arrow keys when panel is open', () => {
101+
// Type some text
102+
cy.focused().type('Sample Text');
103+
waitForReactUpdate(200);
104+
105+
// Open slash panel
106+
cy.focused().type('/');
107+
waitForReactUpdate(500);
108+
109+
// Verify slash panel is open
110+
SlashCommandSelectors.slashPanel().should('be.visible');
111+
112+
// Type search text
113+
cy.focused().type('heading');
114+
waitForReactUpdate(200);
115+
116+
// Press plain ArrowLeft (without Shift) - should be blocked
117+
cy.focused().type('{leftArrow}');
118+
waitForReactUpdate(200);
119+
120+
// Panel should still be open (cursor didn't move away from trigger position)
121+
SlashCommandSelectors.slashPanel().should('be.visible');
122+
123+
// Close panel
124+
cy.focused().type('{esc}');
125+
waitForReactUpdate(200);
126+
127+
// Verify content
128+
EditorSelectors.slateEditor().should('contain.text', 'Sample Text');
129+
});
130+
});
131+
132+
describe('Mention Panel Selection', () => {
133+
it('should allow Shift+Arrow selection when mention panel is open', () => {
134+
// Type some text first
135+
cy.focused().type('Hello ');
136+
waitForReactUpdate(200);
137+
138+
// Open mention panel with @
139+
cy.focused().type('@');
140+
waitForReactUpdate(500);
141+
142+
// Type to search
143+
cy.focused().type('test');
144+
waitForReactUpdate(200);
145+
146+
// Try Shift+Left to select - should work after fix
147+
cy.focused().type('{shift}{leftArrow}{leftArrow}');
148+
waitForReactUpdate(200);
149+
150+
// Close panel
151+
cy.focused().type('{esc}');
152+
waitForReactUpdate(200);
153+
154+
// Editor should still have content
155+
EditorSelectors.slateEditor().should('contain.text', 'Hello');
156+
});
157+
});
158+
});

cypress/e2e/editor/basic/text_editing.cy.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AuthTestUtils } from '../../../support/auth-utils';
2-
import { EditorSelectors, waitForReactUpdate } from '../../../support/selectors';
2+
import { EditorSelectors, waitForReactUpdate, byTestId } from '../../../support/selectors';
33
import { generateRandomEmail, getCmdKey, getWordJumpKey } from '../../../support/test-config';
44

55
describe('Basic Text Editing', () => {
@@ -54,7 +54,7 @@ describe('Basic Text Editing', () => {
5454
// If trigger doesn't work (often Slate relies on beforeInput), try one more fallback:
5555
// type('{del}') again but assume it might need a retry or check.
5656
// Actually, let's trust type('{del}') but double check focus.
57-
cy.get('[data-slate-editor="true"]').focus().type('{del}');
57+
EditorSelectors.slateEditor().focus().type('{del}');
5858
waitForReactUpdate(500);
5959

6060
// "Test |ext"
@@ -153,8 +153,8 @@ describe('Basic Text Editing', () => {
153153

154154
// Use robust selection via data-testid if available, or fallback to text with wait
155155
cy.get('body').then($body => {
156-
if ($body.find('[data-testid="slash-menu-heading1"]').length > 0) {
157-
cy.get('[data-testid="slash-menu-heading1"]').click();
156+
if ($body.find(byTestId('slash-menu-heading1')).length > 0) {
157+
cy.get(byTestId('slash-menu-heading1')).click();
158158
} else if ($body.text().includes('Heading 1')) {
159159
cy.contains('Heading 1').first().click();
160160
} else {
@@ -192,8 +192,8 @@ describe('Basic Text Editing', () => {
192192
waitForReactUpdate(1000);
193193

194194
cy.get('body').then($body => {
195-
if ($body.find('[data-testid="slash-menu-bulletedList"]').length > 0) {
196-
cy.get('[data-testid="slash-menu-bulletedList"]').click();
195+
if ($body.find(byTestId('slash-menu-bulletedList')).length > 0) {
196+
cy.get(byTestId('slash-menu-bulletedList')).click();
197197
} else if ($body.text().includes('Bulleted list')) {
198198
cy.contains('Bulleted list').first().click();
199199
} else {

cypress/e2e/editor/collaboration/tab_sync.cy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('Editor Tab Synchronization', () => {
7373
// 1. Type in Main Window
7474
cy.log('Typing in Main Window');
7575
// Click topLeft to avoid iframe overlay at bottom right
76-
cy.get('[data-slate-editor="true"]').first().click('topLeft', { force: true }).type('Hello from Main');
76+
EditorSelectors.slateEditor().first().click('topLeft', { force: true }).type('Hello from Main');
7777
waitForReactUpdate(2000); // Wait longer for sync
7878

7979
// 2. Verify in Iframe with longer timeout
@@ -86,6 +86,6 @@ describe('Editor Tab Synchronization', () => {
8686
waitForReactUpdate(2000);
8787

8888
// 4. Verify in Main Window with longer timeout
89-
cy.get('[data-slate-editor="true"]', { timeout: 15000 }).should('contain.text', 'Hello from Main and Iframe');
89+
EditorSelectors.slateEditor({ timeout: 15000 }).should('contain.text', 'Hello from Main and Iframe');
9090
});
9191
});

cypress/e2e/editor/commands/editor_commands.cy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AuthTestUtils } from '../../../support/auth-utils';
2-
import { EditorSelectors, waitForReactUpdate } from '../../../support/selectors';
2+
import { BlockSelectors, EditorSelectors, waitForReactUpdate } from '../../../support/selectors';
33
import { generateRandomEmail } from '../../../support/test-config';
44

55
describe('Editor Commands', () => {
@@ -79,7 +79,7 @@ describe('Editor Commands', () => {
7979
cy.focused().type('{shift}{enter}');
8080
waitForReactUpdate(200);
8181
cy.focused().type('Line 2');
82-
cy.get('[data-block-type="paragraph"]').should('have.length', 1);
82+
BlockSelectors.blockByType('paragraph').should('have.length', 1);
8383
cy.contains('Line 1').should('be.visible');
8484
cy.contains('Line 2').should('be.visible');
8585
});

cypress/e2e/editor/cursor/editor_interaction.cy.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AuthTestUtils } from '../../../support/auth-utils';
2-
import { EditorSelectors, waitForReactUpdate } from '../../../support/selectors';
2+
import { BlockSelectors, EditorSelectors, waitForReactUpdate } from '../../../support/selectors';
33
import { generateRandomEmail, getCmdKey } from '../../../support/test-config';
44

55
describe('Editor Navigation & Interaction', () => {
@@ -43,11 +43,11 @@ describe('Editor Navigation & Interaction', () => {
4343
waitForReactUpdate(200);
4444
cy.focused().type('X');
4545
waitForReactUpdate(200);
46-
cy.get('[data-slate-editor="true"]').should('contain.text', 'XStart Middle End');
46+
EditorSelectors.slateEditor().should('contain.text', 'XStart Middle End');
4747
cy.focused().type('{selectall}{rightArrow}');
4848
waitForReactUpdate(200);
4949
cy.focused().type('Y');
50-
cy.get('[data-slate-editor="true"]').should('contain.text', 'XStart Middle EndY');
50+
EditorSelectors.slateEditor().should('contain.text', 'XStart Middle EndY');
5151
});
5252

5353
it('should navigate character by character', () => {
@@ -64,7 +64,7 @@ describe('Editor Navigation & Interaction', () => {
6464
cy.focused().type('-');
6565

6666
// Expect "W-ord"
67-
cy.get('[data-slate-editor="true"]').should('contain.text', 'W-ord');
67+
EditorSelectors.slateEditor().should('contain.text', 'W-ord');
6868
});
6969

7070
it('should select word on double click', () => {
@@ -80,8 +80,8 @@ describe('Editor Navigation & Interaction', () => {
8080
cy.focused().type('Replaced');
8181

8282
// 'SelectMe' should be gone, 'Replaced' should be present
83-
cy.get('[data-slate-editor="true"]').should('contain.text', 'Replaced');
84-
cy.get('[data-slate-editor="true"]').should('not.contain.text', 'SelectMe');
83+
EditorSelectors.slateEditor().should('contain.text', 'Replaced');
84+
EditorSelectors.slateEditor().should('not.contain.text', 'SelectMe');
8585
});
8686

8787
it('should navigate up/down between blocks', () => {
@@ -131,8 +131,8 @@ describe('Editor Navigation & Interaction', () => {
131131
// Type to verify focus
132132
cy.focused().type(' UpTest');
133133
// Verify 'UpTest' appears in Paragraph block and NOT in List Block
134-
cy.get('[data-block-type="paragraph"]').should('contain.text', 'UpTest');
135-
cy.get('[data-block-type="bulleted_list"]').should('not.contain.text', 'UpTest');
134+
BlockSelectors.blockByType('paragraph').should('contain.text', 'UpTest');
135+
BlockSelectors.blockByType('bulleted_list').should('not.contain.text', 'UpTest');
136136

137137
// Test Navigation: Heading -> Paragraph
138138
// Click Heading first to change focus
@@ -144,8 +144,8 @@ describe('Editor Navigation & Interaction', () => {
144144

145145
cy.focused().type(' DownTest');
146146
// Verify 'DownTest' appears in Paragraph block and NOT in Heading Block
147-
cy.get('[data-block-type="paragraph"]').should('contain.text', 'DownTest');
148-
cy.get('[data-block-type="heading"]').should('not.contain.text', 'DownTest');
147+
BlockSelectors.blockByType('paragraph').should('contain.text', 'DownTest');
148+
BlockSelectors.blockByType('heading').should('not.contain.text', 'DownTest');
149149
});
150150
});
151151

@@ -187,7 +187,7 @@ describe('Editor Navigation & Interaction', () => {
187187
describe('Style Interaction', () => {
188188
it.skip('should persist bold style when typing inside bold text', () => {
189189
cy.focused().type('Normal ');
190-
cy.get('[data-slate-editor="true"]').click();
190+
EditorSelectors.slateEditor().click();
191191
cy.focused().type(`${cmdKey}b`);
192192
waitForReactUpdate(200);
193193
cy.focused().type('Bold');
@@ -198,7 +198,7 @@ describe('Editor Navigation & Interaction', () => {
198198
});
199199

200200
it('should reset style when creating a new paragraph', () => {
201-
cy.get('[data-slate-editor="true"]').click();
201+
EditorSelectors.slateEditor().click();
202202
cy.focused().type(`${cmdKey}b`);
203203
waitForReactUpdate(200);
204204
cy.focused().type('Heading Bold');

0 commit comments

Comments
 (0)