Skip to content

Commit 79c767d

Browse files
authored
fix: replace regex and add test coverage (#225)
Fixes regression from #220 Closes #224
1 parent 182ed80 commit 79c767d

File tree

3 files changed

+31
-26
lines changed

3 files changed

+31
-26
lines changed

cypress/integration/textarea.js

+15-21
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ describe("React Textarea Autocomplete", () => {
7272
.should("have.value", "This is test🙄");
7373
});
7474

75+
it("should respect already written tokens", () => {
76+
cy.get(".rta__textarea")
77+
.type("This is test -f{enter} and -s{downarrow}{enter}")
78+
.should("have.value", "This is test -first and -second");
79+
});
80+
7581
it("special character like [, ( should be also possible to use as trigger char", () => {
7682
cy.get(".rta__textarea")
7783
.type("This is test [{enter}")
@@ -99,7 +105,7 @@ describe("React Textarea Autocomplete", () => {
99105
.then(() => {
100106
const endLeft = Cypress.$(".rta__autocomplete").css("left");
101107
cy.get(".rta__autocomplete").should("to.have.css", {
102-
left: startLeft
108+
left: startLeft,
103109
});
104110

105111
expect(startLeft).to.be.equal(endLeft);
@@ -136,10 +142,10 @@ describe("React Textarea Autocomplete", () => {
136142

137143
it("onItemHighlighted should return correct item and trigger", () => {
138144
cy.get(".rta__textarea").type(":ro{uparrow}{uparrow}");
139-
cy.window().then(async win => {
145+
cy.window().then(async (win) => {
140146
const shouldSelectItem = {
141147
currentTrigger: ":",
142-
item: { name: "rofl", char: "🤣" }
148+
item: { name: "rofl", char: "🤣" },
143149
};
144150

145151
expect(win.__lastHighlightedItem).to.deep.equal(shouldSelectItem);
@@ -148,21 +154,17 @@ describe("React Textarea Autocomplete", () => {
148154

149155
it("onItemSelected should return correct item and trigger", () => {
150156
cy.get(".rta__textarea").type(":ro{uparrow}{uparrow}{enter}");
151-
cy.window().then(async win => {
157+
cy.window().then(async (win) => {
152158
const shouldSelectItem = {
153159
currentTrigger: ":",
154-
item: { name: "rofl", char: "🤣" }
160+
item: { name: "rofl", char: "🤣" },
155161
};
156162

157163
expect(win.__lastSelectedItem).to.deep.equal(shouldSelectItem);
158164
});
159165
});
160166

161167
it("should have place caret before outputted word", () => {
162-
/**
163-
* This is probably Cypress bug (1.0.2)
164-
* This test needs to be run in headed mode, otherwise fails
165-
*/
166168
cy.get('[data-test="caretStart"]').click();
167169

168170
cy.get(".rta__textarea").type("This is test :ro{downarrow}{downarrow}");
@@ -173,10 +175,6 @@ describe("React Textarea Autocomplete", () => {
173175
});
174176

175177
it("should place caret after word", () => {
176-
/**
177-
* This is probably Cypress bug (1.0.2)
178-
* This test needs to be run in headed mode, otherwise fails
179-
*/
180178
cy.get('[data-test="caretEnd"]').click();
181179

182180
cy.get(".rta__textarea").type("This is test :ro{downarrow}{downarrow}");
@@ -187,10 +185,6 @@ describe("React Textarea Autocomplete", () => {
187185
});
188186

189187
it("should caret after word with a space", () => {
190-
/**
191-
* This is probably Cypress bug (1.0.2)
192-
* This test needs to be run in headed mode, otherwise fails
193-
*/
194188
cy.get('[data-test="caretNext"]').click();
195189

196190
cy.get(".rta__textarea").type("This is test :ro{downarrow}{downarrow}");
@@ -266,7 +260,7 @@ describe("React Textarea Autocomplete", () => {
266260
cy.get(".rta__textarea").type(
267261
`${repeat("{backspace}", 13)} again {downarrow}{enter}`,
268262
{
269-
force: true
263+
force: true,
270264
}
271265
);
272266
cy.get(".rta__textarea").should("have.value", "This is test /");
@@ -366,7 +360,7 @@ describe("React Textarea Autocomplete", () => {
366360
.get("li:nth-child(1)")
367361
.click();
368362
cy.get(".rta__textarea").type(`${repeat("\n", 5)} test :a`, {
369-
force: true
363+
force: true,
370364
});
371365
cy.get(".rta__autocomplete").should(
372366
"have.class",
@@ -395,13 +389,13 @@ describe("React Textarea Autocomplete", () => {
395389
});
396390

397391
it("event is successfully blocked", () => {
398-
cy.window().then(async win => {
392+
cy.window().then(async (win) => {
399393
const spy = cy.spy(win.console, "log");
400394

401395
await cy
402396
.get(".rta__textarea")
403397
.type(":ro{uparrow}{uparrow}{enter}")
404-
.then(e => {
398+
.then((e) => {
405399
// the last console.log call should not be `pressed "enter"` because that event is blocked because it's happening in autocomplete.
406400
expect(spy.lastCall.args).to.eql([`pressed "o"`]);
407401
});

example/App.jsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,17 @@ class App extends React.Component {
411411
text: `${trigger}${item.name}`,
412412
caretPosition: "end"
413413
})
414-
}
414+
},
415+
"-": {
416+
dataProvider: token => {
417+
return [
418+
{ name: "f", char: "-first" },
419+
{ name: "s", char: "-second" }
420+
];
421+
},
422+
component: Item,
423+
output: this._outputCaretEnd
424+
},
415425
}}
416426
/>
417427
{!showSecondTextarea ? null : (

src/Textarea.jsx

+5-4
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ class ReactTextareaAutocomplete extends React.Component<
384384

385385
_onSelect = (item: Object | string) => {
386386
const { selectionEnd, currentTrigger, value: textareaValue } = this.state;
387-
const { trigger, onItemSelected } = this.props;
387+
const { onItemSelected } = this.props;
388388

389389
if (!currentTrigger) return;
390390

@@ -437,14 +437,15 @@ class ReactTextareaAutocomplete extends React.Component<
437437

438438
/**
439439
* It's important to escape the currentTrigger char for chars like [, (,...
440+
* This is a ridiculous dark magic, basically we found position of the last current token (from current trigger) and then we replace the text from that position (calculating the offset)
440441
*/
441442
const escapedCurrentTrigger = escapeRegex(currentTrigger);
442-
const escapedCurrentTriggerWithWhitespace = escapedCurrentTrigger + (trigger[currentTrigger].allowWhitespace ? "" : "\\s");
443+
const triggerOffset = textToModify.length - textToModify.lastIndexOf(currentTrigger);
443444
const startOfTokenPosition = textToModify.search(
444445
new RegExp(
445-
`${escapedCurrentTrigger}((?!${escapedCurrentTriggerWithWhitespace}).)*$`
446+
`(?!${escapedCurrentTrigger})$`
446447
)
447-
);
448+
) - triggerOffset;
448449

449450
// we add space after emoji is selected if a caret position is next
450451
const newTokenString =

0 commit comments

Comments
 (0)