@@ -227,6 +227,133 @@ describe("reducer", () => {
227227 } ) ;
228228} ) ;
229229
230+ describe ( "paste" , ( ) => {
231+ test ( "keeps literal double quotes when pasting a single value" , ( ) => {
232+ const state : Types . StoreState = {
233+ ...INITIAL_STATE ,
234+ active : Point . ORIGIN ,
235+ model : new Model ( createFormulaParser , createEmptyMatrix ( 1 , 1 ) ) ,
236+ selected : new RangeSelection ( new PointRange ( Point . ORIGIN , Point . ORIGIN ) ) ,
237+ } ;
238+
239+ const nextState = reducer ( state , Actions . paste ( '"Value"' ) ) ;
240+
241+ expect ( Matrix . get ( Point . ORIGIN , nextState . model . data ) ) . toEqual ( {
242+ value : '"Value"' ,
243+ } ) ;
244+ expect ( Matrix . get ( Point . ORIGIN , nextState . model . evaluatedData ) ) . toEqual ( {
245+ value : '"Value"' ,
246+ } ) ;
247+ } ) ;
248+
249+ test ( "keeps legacy behavior for quoted numeric single values" , ( ) => {
250+ const state : Types . StoreState = {
251+ ...INITIAL_STATE ,
252+ active : Point . ORIGIN ,
253+ model : new Model ( createFormulaParser , createEmptyMatrix ( 1 , 1 ) ) ,
254+ selected : new RangeSelection ( new PointRange ( Point . ORIGIN , Point . ORIGIN ) ) ,
255+ } ;
256+
257+ const nextState = reducer ( state , Actions . paste ( '"123"' ) ) ;
258+
259+ expect ( Matrix . get ( Point . ORIGIN , nextState . model . data ) ) . toEqual ( {
260+ value : "123" ,
261+ } ) ;
262+ } ) ;
263+
264+ test ( "keeps escaped quotes as raw text when pasting a single value" , ( ) => {
265+ const state : Types . StoreState = {
266+ ...INITIAL_STATE ,
267+ active : Point . ORIGIN ,
268+ model : new Model ( createFormulaParser , createEmptyMatrix ( 1 , 1 ) ) ,
269+ selected : new RangeSelection ( new PointRange ( Point . ORIGIN , Point . ORIGIN ) ) ,
270+ } ;
271+
272+ const nextState = reducer (
273+ state ,
274+ Actions . paste ( '"He said ""hello"""' )
275+ ) ;
276+
277+ expect ( Matrix . get ( Point . ORIGIN , nextState . model . data ) ) . toEqual ( {
278+ value : '"He said ""hello"""' ,
279+ } ) ;
280+ } ) ;
281+
282+ test ( "preserves literal quotes in simple quoted multi-cell fields" , ( ) => {
283+ const state : Types . StoreState = {
284+ ...INITIAL_STATE ,
285+ active : Point . ORIGIN ,
286+ model : new Model ( createFormulaParser , createEmptyMatrix ( 1 , 2 ) ) ,
287+ selected : new RangeSelection ( new PointRange ( Point . ORIGIN , Point . ORIGIN ) ) ,
288+ } ;
289+
290+ const nextState = reducer ( state , Actions . paste ( '"aa"\ta' ) ) ;
291+
292+ expect ( Matrix . get ( Point . ORIGIN , nextState . model . data ) ) . toEqual ( {
293+ value : '"aa"' ,
294+ } ) ;
295+ expect (
296+ Matrix . get ( { row : Point . ORIGIN . row , column : Point . ORIGIN . column + 1 } , nextState . model . data )
297+ ) . toEqual ( {
298+ value : "a" ,
299+ } ) ;
300+ } ) ;
301+
302+ test ( "unquotes quoted fields that protect tab separators" , ( ) => {
303+ const state : Types . StoreState = {
304+ ...INITIAL_STATE ,
305+ active : Point . ORIGIN ,
306+ model : new Model ( createFormulaParser , createEmptyMatrix ( 1 , 2 ) ) ,
307+ selected : new RangeSelection ( new PointRange ( Point . ORIGIN , Point . ORIGIN ) ) ,
308+ } ;
309+
310+ const nextState = reducer ( state , Actions . paste ( '"Value\t1"\tValue2' ) ) ;
311+
312+ expect ( Matrix . get ( Point . ORIGIN , nextState . model . data ) ) . toEqual ( {
313+ value : "Value\t1" ,
314+ } ) ;
315+ expect (
316+ Matrix . get ( { row : Point . ORIGIN . row , column : Point . ORIGIN . column + 1 } , nextState . model . data )
317+ ) . toEqual ( {
318+ value : "Value2" ,
319+ } ) ;
320+ } ) ;
321+
322+ test ( "preserves literal quotes in real-world multi-row paste" , ( ) => {
323+ const state : Types . StoreState = {
324+ ...INITIAL_STATE ,
325+ active : Point . ORIGIN ,
326+ model : new Model ( createFormulaParser , createEmptyMatrix ( 5 , 5 ) ) ,
327+ selected : new RangeSelection ( new PointRange ( Point . ORIGIN , Point . ORIGIN ) ) ,
328+ } ;
329+ const text = [
330+ '"aa"\ta\tDrugA\tDrugA\t' ,
331+ '"aa"\ta\tDrugA\tDrugA\t' ,
332+ '"aa"\ta\tDrugA\tDrugA\t' ,
333+ '\t\tDrugA\tDrugA\tDrugA' ,
334+ '\t\t\tDrugA\tDrugA' ,
335+ ] . join ( "\n" ) ;
336+
337+ const nextState = reducer ( state , Actions . paste ( text ) ) ;
338+
339+ expect ( Matrix . get ( { row : 0 , column : 0 } , nextState . model . data ) ) . toEqual ( {
340+ value : '"aa"' ,
341+ } ) ;
342+ expect ( Matrix . get ( { row : 1 , column : 0 } , nextState . model . data ) ) . toEqual ( {
343+ value : '"aa"' ,
344+ } ) ;
345+ expect ( Matrix . get ( { row : 2 , column : 0 } , nextState . model . data ) ) . toEqual ( {
346+ value : '"aa"' ,
347+ } ) ;
348+ expect ( Matrix . get ( { row : 3 , column : 2 } , nextState . model . data ) ) . toEqual ( {
349+ value : "DrugA" ,
350+ } ) ;
351+ expect ( Matrix . get ( { row : 4 , column : 3 } , nextState . model . data ) ) . toEqual ( {
352+ value : "DrugA" ,
353+ } ) ;
354+ } ) ;
355+ } ) ;
356+
230357describe ( "hasKeyDownHandler" , ( ) => {
231358 const cases = [
232359 [ "returns true for handled key" , INITIAL_STATE , "Enter" , true ] ,
0 commit comments