@marimo-team/frontend 0.24.1-dev8 → 0.24.1-dev9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index-h_pKEkiy.js +38 -0
- package/dist/assets/{index-WoaGtlKd.css → index-n_AVX7aW.css} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/plugins/impl/MatrixPlugin.tsx +256 -35
- package/src/plugins/impl/__tests__/MatrixPlugin.test.tsx +540 -3
- package/dist/assets/index-BBkaSMl2.js +0 -38
|
@@ -177,7 +177,7 @@ describe("MatrixPlugin", () => {
|
|
|
177
177
|
|
|
178
178
|
it("validates with zod schema", () => {
|
|
179
179
|
const plugin = new MatrixPlugin();
|
|
180
|
-
const
|
|
180
|
+
const payload = {
|
|
181
181
|
initialValue: [
|
|
182
182
|
[1, 2],
|
|
183
183
|
[3, 4],
|
|
@@ -198,8 +198,12 @@ describe("MatrixPlugin", () => {
|
|
|
198
198
|
[false, false],
|
|
199
199
|
[false, false],
|
|
200
200
|
],
|
|
201
|
-
}
|
|
202
|
-
|
|
201
|
+
};
|
|
202
|
+
// debounce is optional and defaults off
|
|
203
|
+
expect(plugin.validator.parse(payload).debounce).toBe(false);
|
|
204
|
+
expect(plugin.validator.safeParse({ ...payload, step: null }).success).toBe(
|
|
205
|
+
false,
|
|
206
|
+
);
|
|
203
207
|
});
|
|
204
208
|
|
|
205
209
|
it("displays values in scientific notation", () => {
|
|
@@ -297,6 +301,26 @@ describe("MatrixPlugin", () => {
|
|
|
297
301
|
]);
|
|
298
302
|
});
|
|
299
303
|
|
|
304
|
+
it("ArrowUp preserves significant digits in large cell values", () => {
|
|
305
|
+
const plugin = new MatrixPlugin();
|
|
306
|
+
const setValueMock = vi.fn();
|
|
307
|
+
const props = makeProps({
|
|
308
|
+
value: [
|
|
309
|
+
[1_234_567_890_123, 0],
|
|
310
|
+
[0, 0],
|
|
311
|
+
],
|
|
312
|
+
setValue: setValueMock,
|
|
313
|
+
});
|
|
314
|
+
const { getByTestId } = render(plugin.render(props));
|
|
315
|
+
|
|
316
|
+
fireEvent.keyDown(getByTestId("matrix-cell-0-0"), { key: "ArrowUp" });
|
|
317
|
+
|
|
318
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
319
|
+
[1_234_567_890_124, 0],
|
|
320
|
+
[0, 0],
|
|
321
|
+
]);
|
|
322
|
+
});
|
|
323
|
+
|
|
300
324
|
it("ArrowDown decrements cell value", () => {
|
|
301
325
|
const plugin = new MatrixPlugin();
|
|
302
326
|
const setValueMock = vi.fn();
|
|
@@ -317,6 +341,45 @@ describe("MatrixPlugin", () => {
|
|
|
317
341
|
]);
|
|
318
342
|
});
|
|
319
343
|
|
|
344
|
+
it("ArrowRight and ArrowLeft step like ArrowUp and ArrowDown", () => {
|
|
345
|
+
const plugin = new MatrixPlugin();
|
|
346
|
+
const setValueMock = vi.fn();
|
|
347
|
+
const props = makeProps({
|
|
348
|
+
value: [
|
|
349
|
+
[5, 0],
|
|
350
|
+
[0, 0],
|
|
351
|
+
],
|
|
352
|
+
setValue: setValueMock,
|
|
353
|
+
});
|
|
354
|
+
const { getByTestId } = render(plugin.render(props));
|
|
355
|
+
const cell = getByTestId("matrix-cell-0-0");
|
|
356
|
+
|
|
357
|
+
fireEvent.keyDown(cell, { key: "ArrowRight" });
|
|
358
|
+
expect(setValueMock).toHaveBeenLastCalledWith([
|
|
359
|
+
[6, 0],
|
|
360
|
+
[0, 0],
|
|
361
|
+
]);
|
|
362
|
+
|
|
363
|
+
// Modifier scaling applies to the horizontal pair too: 5 - 10x step
|
|
364
|
+
fireEvent.keyDown(cell, { key: "ArrowLeft", shiftKey: true });
|
|
365
|
+
expect(setValueMock).toHaveBeenLastCalledWith([
|
|
366
|
+
[-5, 0],
|
|
367
|
+
[0, 0],
|
|
368
|
+
]);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it("clicking a cell focuses it for keyboard stepping", () => {
|
|
372
|
+
const plugin = new MatrixPlugin();
|
|
373
|
+
const props = makeProps();
|
|
374
|
+
const { getByTestId } = render(plugin.render(props));
|
|
375
|
+
const cell = getByTestId("matrix-cell-0-0");
|
|
376
|
+
|
|
377
|
+
fireEvent.pointerDown(cell, { clientX: 100, pointerId: 1 });
|
|
378
|
+
fireEvent.pointerUp(getByTestId("marimo-plugin-matrix"));
|
|
379
|
+
|
|
380
|
+
expect(document.activeElement).toBe(cell);
|
|
381
|
+
});
|
|
382
|
+
|
|
320
383
|
it("disabled cells ignore pointer and keyboard input", () => {
|
|
321
384
|
const plugin = new MatrixPlugin();
|
|
322
385
|
const setValueMock = vi.fn();
|
|
@@ -386,6 +449,38 @@ describe("MatrixPlugin", () => {
|
|
|
386
449
|
]);
|
|
387
450
|
});
|
|
388
451
|
|
|
452
|
+
it("with debounce, defers setValue until the drag ends", () => {
|
|
453
|
+
const plugin = new MatrixPlugin();
|
|
454
|
+
const setValueMock = vi.fn();
|
|
455
|
+
const props = makeProps({
|
|
456
|
+
value: [
|
|
457
|
+
[0, 0],
|
|
458
|
+
[0, 0],
|
|
459
|
+
],
|
|
460
|
+
setValue: setValueMock,
|
|
461
|
+
data: {
|
|
462
|
+
...makeProps().data,
|
|
463
|
+
debounce: true,
|
|
464
|
+
},
|
|
465
|
+
});
|
|
466
|
+
const { getByTestId } = render(plugin.render(props));
|
|
467
|
+
const cell = getByTestId("matrix-cell-0-0");
|
|
468
|
+
const container = getByTestId("marimo-plugin-matrix");
|
|
469
|
+
|
|
470
|
+
fireEvent.pointerDown(cell, { clientX: 100, pointerId: 1 });
|
|
471
|
+
fireEvent.pointerMove(container, { clientX: 130 });
|
|
472
|
+
|
|
473
|
+
// The dragged value renders locally but is not emitted yet.
|
|
474
|
+
expect(cell.textContent).toBe("3.0");
|
|
475
|
+
expect(setValueMock).not.toHaveBeenCalled();
|
|
476
|
+
|
|
477
|
+
fireEvent.pointerUp(container);
|
|
478
|
+
expect(setValueMock).toHaveBeenCalledExactlyOnceWith([
|
|
479
|
+
[3, 0],
|
|
480
|
+
[0, 0],
|
|
481
|
+
]);
|
|
482
|
+
});
|
|
483
|
+
|
|
389
484
|
it("sets aria attributes on cells", () => {
|
|
390
485
|
const plugin = new MatrixPlugin();
|
|
391
486
|
const props = makeProps({
|
|
@@ -413,3 +508,445 @@ describe("MatrixPlugin", () => {
|
|
|
413
508
|
expect(cell.getAttribute("tabindex")).toBe("0");
|
|
414
509
|
});
|
|
415
510
|
});
|
|
511
|
+
|
|
512
|
+
describe("MatrixPlugin cell editing", () => {
|
|
513
|
+
it("double-click opens an editor prefilled with the exact value", () => {
|
|
514
|
+
const plugin = new MatrixPlugin();
|
|
515
|
+
const props = makeProps();
|
|
516
|
+
const { getByTestId } = render(plugin.render(props));
|
|
517
|
+
|
|
518
|
+
fireEvent.doubleClick(getByTestId("matrix-cell-0-0"));
|
|
519
|
+
|
|
520
|
+
const input = getByTestId("matrix-input-0-0") as HTMLInputElement;
|
|
521
|
+
expect(input.value).toBe("1");
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it("commits a typed scientific-notation value on Enter", () => {
|
|
525
|
+
const plugin = new MatrixPlugin();
|
|
526
|
+
const setValueMock = vi.fn();
|
|
527
|
+
const props = makeProps({ setValue: setValueMock });
|
|
528
|
+
const { getByTestId, queryByTestId } = render(plugin.render(props));
|
|
529
|
+
|
|
530
|
+
fireEvent.doubleClick(getByTestId("matrix-cell-0-0"));
|
|
531
|
+
const input = getByTestId("matrix-input-0-0");
|
|
532
|
+
fireEvent.change(input, { target: { value: "2.32e7" } });
|
|
533
|
+
fireEvent.keyDown(input, { key: "Enter" });
|
|
534
|
+
|
|
535
|
+
// Exactly once: the blur fired while refocusing the cell must not
|
|
536
|
+
// commit a second time.
|
|
537
|
+
expect(setValueMock).toHaveBeenCalledExactlyOnceWith([
|
|
538
|
+
[23_200_000, 2],
|
|
539
|
+
[3, 4],
|
|
540
|
+
]);
|
|
541
|
+
expect(queryByTestId("matrix-input-0-0")).toBeNull();
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it("does not snap typed values to step", () => {
|
|
545
|
+
const plugin = new MatrixPlugin();
|
|
546
|
+
const setValueMock = vi.fn();
|
|
547
|
+
const props = makeProps({ setValue: setValueMock });
|
|
548
|
+
const { getByTestId } = render(plugin.render(props));
|
|
549
|
+
|
|
550
|
+
fireEvent.doubleClick(getByTestId("matrix-cell-0-0"));
|
|
551
|
+
const input = getByTestId("matrix-input-0-0");
|
|
552
|
+
fireEvent.change(input, { target: { value: "2.5" } });
|
|
553
|
+
fireEvent.keyDown(input, { key: "Enter" });
|
|
554
|
+
|
|
555
|
+
// step is 1, but the typed value is exact
|
|
556
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
557
|
+
[2.5, 2],
|
|
558
|
+
[3, 4],
|
|
559
|
+
]);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it("commits on blur", () => {
|
|
563
|
+
const plugin = new MatrixPlugin();
|
|
564
|
+
const setValueMock = vi.fn();
|
|
565
|
+
const props = makeProps({ setValue: setValueMock });
|
|
566
|
+
const { getByTestId, queryByTestId } = render(plugin.render(props));
|
|
567
|
+
|
|
568
|
+
fireEvent.doubleClick(getByTestId("matrix-cell-0-0"));
|
|
569
|
+
const input = getByTestId("matrix-input-0-0");
|
|
570
|
+
fireEvent.change(input, { target: { value: "42" } });
|
|
571
|
+
fireEvent.blur(input);
|
|
572
|
+
|
|
573
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
574
|
+
[42, 2],
|
|
575
|
+
[3, 4],
|
|
576
|
+
]);
|
|
577
|
+
expect(queryByTestId("matrix-input-0-0")).toBeNull();
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it("Escape cancels without committing", () => {
|
|
581
|
+
const plugin = new MatrixPlugin();
|
|
582
|
+
const setValueMock = vi.fn();
|
|
583
|
+
const props = makeProps({ setValue: setValueMock });
|
|
584
|
+
const { getByTestId, queryByTestId } = render(plugin.render(props));
|
|
585
|
+
|
|
586
|
+
fireEvent.doubleClick(getByTestId("matrix-cell-0-0"));
|
|
587
|
+
const input = getByTestId("matrix-input-0-0");
|
|
588
|
+
fireEvent.change(input, { target: { value: "42" } });
|
|
589
|
+
fireEvent.keyDown(input, { key: "Escape" });
|
|
590
|
+
|
|
591
|
+
expect(setValueMock).not.toHaveBeenCalled();
|
|
592
|
+
expect(queryByTestId("matrix-input-0-0")).toBeNull();
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it("discards invalid input and closes the editor", () => {
|
|
596
|
+
const plugin = new MatrixPlugin();
|
|
597
|
+
const setValueMock = vi.fn();
|
|
598
|
+
const props = makeProps({ setValue: setValueMock });
|
|
599
|
+
const { getByTestId, queryByTestId } = render(plugin.render(props));
|
|
600
|
+
|
|
601
|
+
fireEvent.doubleClick(getByTestId("matrix-cell-0-0"));
|
|
602
|
+
const input = getByTestId("matrix-input-0-0");
|
|
603
|
+
fireEvent.change(input, { target: { value: "abc" } });
|
|
604
|
+
fireEvent.keyDown(input, { key: "Enter" });
|
|
605
|
+
|
|
606
|
+
expect(setValueMock).not.toHaveBeenCalled();
|
|
607
|
+
expect(queryByTestId("matrix-input-0-0")).toBeNull();
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it("clamps typed values to bounds", () => {
|
|
611
|
+
const plugin = new MatrixPlugin();
|
|
612
|
+
const setValueMock = vi.fn();
|
|
613
|
+
const props = makeProps({
|
|
614
|
+
setValue: setValueMock,
|
|
615
|
+
data: {
|
|
616
|
+
...makeProps().data,
|
|
617
|
+
maxValue: [
|
|
618
|
+
[10, 10],
|
|
619
|
+
[10, 10],
|
|
620
|
+
],
|
|
621
|
+
},
|
|
622
|
+
});
|
|
623
|
+
const { getByTestId } = render(plugin.render(props));
|
|
624
|
+
|
|
625
|
+
fireEvent.doubleClick(getByTestId("matrix-cell-0-0"));
|
|
626
|
+
const input = getByTestId("matrix-input-0-0");
|
|
627
|
+
fireEvent.change(input, { target: { value: "50" } });
|
|
628
|
+
fireEvent.keyDown(input, { key: "Enter" });
|
|
629
|
+
|
|
630
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
631
|
+
[10, 2],
|
|
632
|
+
[3, 4],
|
|
633
|
+
]);
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
it("mirrors typed values in symmetric mode", () => {
|
|
637
|
+
const plugin = new MatrixPlugin();
|
|
638
|
+
const setValueMock = vi.fn();
|
|
639
|
+
const props = makeProps({
|
|
640
|
+
value: [
|
|
641
|
+
[0, 0],
|
|
642
|
+
[0, 0],
|
|
643
|
+
],
|
|
644
|
+
setValue: setValueMock,
|
|
645
|
+
data: {
|
|
646
|
+
...makeProps().data,
|
|
647
|
+
symmetric: true,
|
|
648
|
+
},
|
|
649
|
+
});
|
|
650
|
+
const { getByTestId } = render(plugin.render(props));
|
|
651
|
+
|
|
652
|
+
fireEvent.doubleClick(getByTestId("matrix-cell-0-1"));
|
|
653
|
+
const input = getByTestId("matrix-input-0-1");
|
|
654
|
+
fireEvent.change(input, { target: { value: "7" } });
|
|
655
|
+
fireEvent.keyDown(input, { key: "Enter" });
|
|
656
|
+
|
|
657
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
658
|
+
[0, 7],
|
|
659
|
+
[7, 0],
|
|
660
|
+
]);
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
it("keeps the symmetric mirror within the transpose cell's bounds", () => {
|
|
664
|
+
// Asymmetric per-cell bounds on a symmetric matrix: [0][1] allows up to
|
|
665
|
+
// 100, but its transpose [1][0] is capped at 5. Editing [0][1] must not
|
|
666
|
+
// smuggle an out-of-bounds value into the mirror, and the pair must stay
|
|
667
|
+
// equal — so the shared value is clamped to the intersection (max 5).
|
|
668
|
+
const plugin = new MatrixPlugin();
|
|
669
|
+
const setValueMock = vi.fn();
|
|
670
|
+
const props = makeProps({
|
|
671
|
+
value: [
|
|
672
|
+
[0, 0],
|
|
673
|
+
[0, 0],
|
|
674
|
+
],
|
|
675
|
+
setValue: setValueMock,
|
|
676
|
+
data: {
|
|
677
|
+
...makeProps().data,
|
|
678
|
+
symmetric: true,
|
|
679
|
+
maxValue: [
|
|
680
|
+
[100, 100],
|
|
681
|
+
[5, 100],
|
|
682
|
+
],
|
|
683
|
+
},
|
|
684
|
+
});
|
|
685
|
+
const { getByTestId } = render(plugin.render(props));
|
|
686
|
+
|
|
687
|
+
fireEvent.doubleClick(getByTestId("matrix-cell-0-1"));
|
|
688
|
+
const input = getByTestId("matrix-input-0-1");
|
|
689
|
+
fireEvent.change(input, { target: { value: "7" } });
|
|
690
|
+
fireEvent.keyDown(input, { key: "Enter" });
|
|
691
|
+
|
|
692
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
693
|
+
[0, 5],
|
|
694
|
+
[5, 0],
|
|
695
|
+
]);
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
it("emits nothing when clamping leaves the value unchanged", () => {
|
|
699
|
+
// Already at the transpose's cap of 5: dragging or typing past it must
|
|
700
|
+
// not fire setValue with an identical matrix.
|
|
701
|
+
const plugin = new MatrixPlugin();
|
|
702
|
+
const setValueMock = vi.fn();
|
|
703
|
+
const props = makeProps({
|
|
704
|
+
value: [
|
|
705
|
+
[0, 5],
|
|
706
|
+
[5, 0],
|
|
707
|
+
],
|
|
708
|
+
setValue: setValueMock,
|
|
709
|
+
data: {
|
|
710
|
+
...makeProps().data,
|
|
711
|
+
symmetric: true,
|
|
712
|
+
maxValue: [
|
|
713
|
+
[100, 100],
|
|
714
|
+
[5, 100],
|
|
715
|
+
],
|
|
716
|
+
},
|
|
717
|
+
});
|
|
718
|
+
const { getByTestId } = render(plugin.render(props));
|
|
719
|
+
const cell = getByTestId("matrix-cell-0-1");
|
|
720
|
+
const container = getByTestId("marimo-plugin-matrix");
|
|
721
|
+
|
|
722
|
+
fireEvent.pointerDown(cell, { clientX: 100, pointerId: 1 });
|
|
723
|
+
fireEvent.pointerMove(container, { clientX: 130 });
|
|
724
|
+
fireEvent.pointerUp(container);
|
|
725
|
+
|
|
726
|
+
fireEvent.doubleClick(cell);
|
|
727
|
+
const input = getByTestId("matrix-input-0-1");
|
|
728
|
+
fireEvent.change(input, { target: { value: "7" } });
|
|
729
|
+
fireEvent.keyDown(input, { key: "Enter" });
|
|
730
|
+
|
|
731
|
+
expect(setValueMock).not.toHaveBeenCalled();
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
it("Enter opens the editor from the keyboard", () => {
|
|
735
|
+
const plugin = new MatrixPlugin();
|
|
736
|
+
const props = makeProps();
|
|
737
|
+
const { getByTestId } = render(plugin.render(props));
|
|
738
|
+
|
|
739
|
+
fireEvent.keyDown(getByTestId("matrix-cell-0-0"), { key: "Enter" });
|
|
740
|
+
|
|
741
|
+
const input = getByTestId("matrix-input-0-0") as HTMLInputElement;
|
|
742
|
+
expect(input.value).toBe("1");
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
it("typing a digit starts editing seeded with that digit", () => {
|
|
746
|
+
const plugin = new MatrixPlugin();
|
|
747
|
+
const props = makeProps();
|
|
748
|
+
const { getByTestId } = render(plugin.render(props));
|
|
749
|
+
|
|
750
|
+
fireEvent.keyDown(getByTestId("matrix-cell-0-0"), { key: "5" });
|
|
751
|
+
|
|
752
|
+
const input = getByTestId("matrix-input-0-0") as HTMLInputElement;
|
|
753
|
+
expect(input.value).toBe("5");
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
it("does not open an editor on disabled cells", () => {
|
|
757
|
+
const plugin = new MatrixPlugin();
|
|
758
|
+
const props = makeProps({
|
|
759
|
+
data: {
|
|
760
|
+
...makeProps().data,
|
|
761
|
+
disabled: [
|
|
762
|
+
[true, false],
|
|
763
|
+
[false, false],
|
|
764
|
+
],
|
|
765
|
+
},
|
|
766
|
+
});
|
|
767
|
+
const { getByTestId, queryByTestId } = render(plugin.render(props));
|
|
768
|
+
|
|
769
|
+
fireEvent.doubleClick(getByTestId("matrix-cell-0-0"));
|
|
770
|
+
expect(queryByTestId("matrix-input-0-0")).toBeNull();
|
|
771
|
+
|
|
772
|
+
fireEvent.keyDown(getByTestId("matrix-cell-0-0"), { key: "Enter" });
|
|
773
|
+
expect(queryByTestId("matrix-input-0-0")).toBeNull();
|
|
774
|
+
});
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
describe("MatrixPlugin modifier scrubbing", () => {
|
|
778
|
+
it("shift+drag scrubs at 10x step", () => {
|
|
779
|
+
const plugin = new MatrixPlugin();
|
|
780
|
+
const setValueMock = vi.fn();
|
|
781
|
+
const props = makeProps({
|
|
782
|
+
value: [
|
|
783
|
+
[0, 0],
|
|
784
|
+
[0, 0],
|
|
785
|
+
],
|
|
786
|
+
setValue: setValueMock,
|
|
787
|
+
});
|
|
788
|
+
const { getByTestId } = render(plugin.render(props));
|
|
789
|
+
const cell = getByTestId("matrix-cell-0-0");
|
|
790
|
+
const container = getByTestId("marimo-plugin-matrix");
|
|
791
|
+
|
|
792
|
+
fireEvent.pointerDown(cell, { clientX: 100, pointerId: 1, shiftKey: true });
|
|
793
|
+
fireEvent.pointerMove(container, { clientX: 130, shiftKey: true });
|
|
794
|
+
fireEvent.pointerUp(container);
|
|
795
|
+
|
|
796
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
797
|
+
[30, 0],
|
|
798
|
+
[0, 0],
|
|
799
|
+
]);
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
it("alt+drag scrubs at 0.1x step", () => {
|
|
803
|
+
const plugin = new MatrixPlugin();
|
|
804
|
+
const setValueMock = vi.fn();
|
|
805
|
+
const props = makeProps({
|
|
806
|
+
value: [
|
|
807
|
+
[0, 0],
|
|
808
|
+
[0, 0],
|
|
809
|
+
],
|
|
810
|
+
setValue: setValueMock,
|
|
811
|
+
});
|
|
812
|
+
const { getByTestId } = render(plugin.render(props));
|
|
813
|
+
const cell = getByTestId("matrix-cell-0-0");
|
|
814
|
+
const container = getByTestId("marimo-plugin-matrix");
|
|
815
|
+
|
|
816
|
+
fireEvent.pointerDown(cell, { clientX: 100, pointerId: 1, altKey: true });
|
|
817
|
+
fireEvent.pointerMove(container, { clientX: 130, altKey: true });
|
|
818
|
+
fireEvent.pointerUp(container);
|
|
819
|
+
|
|
820
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
821
|
+
[0.3, 0],
|
|
822
|
+
[0, 0],
|
|
823
|
+
]);
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
it("pressing shift mid-drag rescales future movement without jumping", () => {
|
|
827
|
+
const plugin = new MatrixPlugin();
|
|
828
|
+
const setValueMock = vi.fn();
|
|
829
|
+
const props = makeProps({
|
|
830
|
+
value: [
|
|
831
|
+
[0, 0],
|
|
832
|
+
[0, 0],
|
|
833
|
+
],
|
|
834
|
+
setValue: setValueMock,
|
|
835
|
+
});
|
|
836
|
+
const { getByTestId } = render(plugin.render(props));
|
|
837
|
+
const cell = getByTestId("matrix-cell-0-0");
|
|
838
|
+
const container = getByTestId("marimo-plugin-matrix");
|
|
839
|
+
|
|
840
|
+
fireEvent.pointerDown(cell, { clientX: 100, pointerId: 1 });
|
|
841
|
+
fireEvent.pointerMove(container, { clientX: 130 }); // +3
|
|
842
|
+
fireEvent.pointerMove(container, { clientX: 130, shiftKey: true }); // rebase
|
|
843
|
+
fireEvent.pointerMove(container, { clientX: 160, shiftKey: true }); // +30
|
|
844
|
+
fireEvent.pointerUp(container);
|
|
845
|
+
|
|
846
|
+
expect(setValueMock).toHaveBeenLastCalledWith([
|
|
847
|
+
[33, 0],
|
|
848
|
+
[0, 0],
|
|
849
|
+
]);
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
it("shift+ArrowUp increments by 10x step", () => {
|
|
853
|
+
const plugin = new MatrixPlugin();
|
|
854
|
+
const setValueMock = vi.fn();
|
|
855
|
+
const props = makeProps({
|
|
856
|
+
value: [
|
|
857
|
+
[5, 0],
|
|
858
|
+
[0, 0],
|
|
859
|
+
],
|
|
860
|
+
setValue: setValueMock,
|
|
861
|
+
});
|
|
862
|
+
const { getByTestId } = render(plugin.render(props));
|
|
863
|
+
|
|
864
|
+
fireEvent.keyDown(getByTestId("matrix-cell-0-0"), {
|
|
865
|
+
key: "ArrowUp",
|
|
866
|
+
shiftKey: true,
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
870
|
+
[15, 0],
|
|
871
|
+
[0, 0],
|
|
872
|
+
]);
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
it("alt+ArrowDown decrements by 0.1x step", () => {
|
|
876
|
+
const plugin = new MatrixPlugin();
|
|
877
|
+
const setValueMock = vi.fn();
|
|
878
|
+
const props = makeProps({
|
|
879
|
+
value: [
|
|
880
|
+
[5, 0],
|
|
881
|
+
[0, 0],
|
|
882
|
+
],
|
|
883
|
+
setValue: setValueMock,
|
|
884
|
+
});
|
|
885
|
+
const { getByTestId } = render(plugin.render(props));
|
|
886
|
+
|
|
887
|
+
fireEvent.keyDown(getByTestId("matrix-cell-0-0"), {
|
|
888
|
+
key: "ArrowDown",
|
|
889
|
+
altKey: true,
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
893
|
+
[4.9, 0],
|
|
894
|
+
[0, 0],
|
|
895
|
+
]);
|
|
896
|
+
});
|
|
897
|
+
|
|
898
|
+
it("keeps tiny step values instead of rounding them to zero", () => {
|
|
899
|
+
// 2e-14 sits below the absolute noise threshold for values of ordinary
|
|
900
|
+
// magnitude; the tolerance must scale with the step to preserve it.
|
|
901
|
+
const plugin = new MatrixPlugin();
|
|
902
|
+
const setValueMock = vi.fn();
|
|
903
|
+
const props = makeProps({
|
|
904
|
+
value: [
|
|
905
|
+
[0, 0],
|
|
906
|
+
[0, 0],
|
|
907
|
+
],
|
|
908
|
+
setValue: setValueMock,
|
|
909
|
+
data: {
|
|
910
|
+
...makeProps().data,
|
|
911
|
+
step: [
|
|
912
|
+
[2e-14, 1],
|
|
913
|
+
[1, 1],
|
|
914
|
+
],
|
|
915
|
+
},
|
|
916
|
+
});
|
|
917
|
+
const { getByTestId } = render(plugin.render(props));
|
|
918
|
+
|
|
919
|
+
fireEvent.keyDown(getByTestId("matrix-cell-0-0"), { key: "ArrowUp" });
|
|
920
|
+
|
|
921
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
922
|
+
[2e-14, 0],
|
|
923
|
+
[0, 0],
|
|
924
|
+
]);
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
it("PageUp and PageDown step by 10x", () => {
|
|
928
|
+
const plugin = new MatrixPlugin();
|
|
929
|
+
const setValueMock = vi.fn();
|
|
930
|
+
const props = makeProps({
|
|
931
|
+
value: [
|
|
932
|
+
[5, 0],
|
|
933
|
+
[0, 0],
|
|
934
|
+
],
|
|
935
|
+
setValue: setValueMock,
|
|
936
|
+
});
|
|
937
|
+
const { getByTestId } = render(plugin.render(props));
|
|
938
|
+
const cell = getByTestId("matrix-cell-0-0");
|
|
939
|
+
|
|
940
|
+
fireEvent.keyDown(cell, { key: "PageUp" });
|
|
941
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
942
|
+
[15, 0],
|
|
943
|
+
[0, 0],
|
|
944
|
+
]);
|
|
945
|
+
|
|
946
|
+
fireEvent.keyDown(cell, { key: "PageDown" });
|
|
947
|
+
expect(setValueMock).toHaveBeenCalledWith([
|
|
948
|
+
[-5, 0],
|
|
949
|
+
[0, 0],
|
|
950
|
+
]);
|
|
951
|
+
});
|
|
952
|
+
});
|