@moni-labs/moni-ui 0.4.14 → 0.4.17
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/custom-elements.json +10 -0
- package/dist/browser/moni-ui.iife.min.js +6 -1
- package/dist/browser/moni-ui.iife.min.js.br +0 -0
- package/dist/browser/moni-ui.iife.min.js.gz +0 -0
- package/dist/browser/moni-ui.iife.min.js.map +1 -1
- package/dist/browser/moni-ui.min.js +75 -23
- package/dist/browser/moni-ui.min.js.br +0 -0
- package/dist/browser/moni-ui.min.js.gz +0 -0
- package/dist/browser/moni-ui.min.js.map +1 -1
- package/dist/cdn/components/moni-bottom-sheet.js +6 -1
- package/dist/cdn/importmap.json +98 -98
- package/dist/components/moni-bottom-sheet.d.ts +19 -5
- package/dist/components/moni-bottom-sheet.d.ts.map +1 -1
- package/dist/components/moni-bottom-sheet.js +169 -30
- package/docs/api.json +13 -2
- package/docs/assets/moni-color-field/shape--square.png +0 -0
- package/docs/assets/moni-file-field/shape--square.png +0 -0
- package/docs/components/bottom-sheet.md +2 -1
- package/package.json +1 -1
- package/src/components/moni-bottom-sheet.test.ts +393 -0
- package/src/components/moni-bottom-sheet.ts +175 -29
|
@@ -432,4 +432,397 @@ describe('moni-bottom-sheet', () => {
|
|
|
432
432
|
expect(closeSpy).not.toHaveBeenCalled();
|
|
433
433
|
expect(el.open).toBe(true);
|
|
434
434
|
});
|
|
435
|
+
|
|
436
|
+
it('detecta arrastre fuera de la pantalla y marca como cerrado', async () => {
|
|
437
|
+
el.handle = true;
|
|
438
|
+
el.open = true;
|
|
439
|
+
await el.updateComplete;
|
|
440
|
+
|
|
441
|
+
const dialog = el.shadowRoot?.querySelector('dialog') as HTMLDialogElement;
|
|
442
|
+
const handle = el.shadowRoot?.querySelector('.handle') as HTMLElement;
|
|
443
|
+
|
|
444
|
+
handle.setPointerCapture = vi.fn();
|
|
445
|
+
handle.releasePointerCapture = vi.fn();
|
|
446
|
+
|
|
447
|
+
vi.spyOn(dialog, 'getBoundingClientRect').mockReturnValue({
|
|
448
|
+
height: 300,
|
|
449
|
+
width: 375,
|
|
450
|
+
x: 0,
|
|
451
|
+
y: 300,
|
|
452
|
+
top: 300,
|
|
453
|
+
bottom: 600,
|
|
454
|
+
left: 0,
|
|
455
|
+
right: 375,
|
|
456
|
+
toJSON: () => {}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
const closeSpy = vi.fn();
|
|
460
|
+
el.addEventListener('close', closeSpy);
|
|
461
|
+
|
|
462
|
+
handle.dispatchEvent(
|
|
463
|
+
new PointerEvent('pointerdown', {
|
|
464
|
+
bubbles: true,
|
|
465
|
+
clientX: 100,
|
|
466
|
+
clientY: 400,
|
|
467
|
+
pointerId: 1
|
|
468
|
+
})
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
// Arrastrar hacia el borde inferior/fuera de pantalla
|
|
472
|
+
dialog.dispatchEvent(
|
|
473
|
+
new PointerEvent('pointermove', {
|
|
474
|
+
bubbles: true,
|
|
475
|
+
clientX: 100,
|
|
476
|
+
clientY: window.innerHeight || 800,
|
|
477
|
+
pointerId: 1
|
|
478
|
+
})
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
// Soltar fuera de la pantalla
|
|
482
|
+
handle.dispatchEvent(
|
|
483
|
+
new PointerEvent('pointerup', {
|
|
484
|
+
bubbles: true,
|
|
485
|
+
clientX: 100,
|
|
486
|
+
clientY: (window.innerHeight || 800) + 50,
|
|
487
|
+
pointerId: 1
|
|
488
|
+
})
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
dialog.dispatchEvent(new Event('transitionend'));
|
|
492
|
+
await el.updateComplete;
|
|
493
|
+
|
|
494
|
+
expect(closeSpy).toHaveBeenCalled();
|
|
495
|
+
expect(el.open).toBe(false);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it('cierra y marca como open=false cuando ocurre pointercancel tras arrastrar hacia abajo', async () => {
|
|
499
|
+
el.handle = true;
|
|
500
|
+
el.open = true;
|
|
501
|
+
await el.updateComplete;
|
|
502
|
+
|
|
503
|
+
const dialog = el.shadowRoot?.querySelector('dialog') as HTMLDialogElement;
|
|
504
|
+
const handle = el.shadowRoot?.querySelector('.handle') as HTMLElement;
|
|
505
|
+
|
|
506
|
+
handle.setPointerCapture = vi.fn();
|
|
507
|
+
handle.releasePointerCapture = vi.fn();
|
|
508
|
+
|
|
509
|
+
vi.spyOn(dialog, 'getBoundingClientRect').mockReturnValue({
|
|
510
|
+
height: 300,
|
|
511
|
+
width: 375,
|
|
512
|
+
x: 0,
|
|
513
|
+
y: 300,
|
|
514
|
+
top: 300,
|
|
515
|
+
bottom: 600,
|
|
516
|
+
left: 0,
|
|
517
|
+
right: 375,
|
|
518
|
+
toJSON: () => {}
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
const closeSpy = vi.fn();
|
|
522
|
+
el.addEventListener('close', closeSpy);
|
|
523
|
+
|
|
524
|
+
handle.dispatchEvent(
|
|
525
|
+
new PointerEvent('pointerdown', {
|
|
526
|
+
bubbles: true,
|
|
527
|
+
clientX: 100,
|
|
528
|
+
clientY: 400,
|
|
529
|
+
pointerId: 1
|
|
530
|
+
})
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
// Arrastre hacia abajo significativo
|
|
534
|
+
dialog.dispatchEvent(
|
|
535
|
+
new PointerEvent('pointermove', {
|
|
536
|
+
bubbles: true,
|
|
537
|
+
clientX: 100,
|
|
538
|
+
clientY: 550, // 150px
|
|
539
|
+
pointerId: 1
|
|
540
|
+
})
|
|
541
|
+
);
|
|
542
|
+
|
|
543
|
+
// El navegador cancela el puntero (por ejemplo, salida de viewport en móvil/pantalla táctil)
|
|
544
|
+
dialog.dispatchEvent(
|
|
545
|
+
new PointerEvent('pointercancel', {
|
|
546
|
+
bubbles: true,
|
|
547
|
+
clientX: 0,
|
|
548
|
+
clientY: 0,
|
|
549
|
+
pointerId: 1
|
|
550
|
+
})
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
dialog.dispatchEvent(new Event('transitionend'));
|
|
554
|
+
await el.updateComplete;
|
|
555
|
+
|
|
556
|
+
expect(closeSpy).toHaveBeenCalled();
|
|
557
|
+
expect(el.open).toBe(false);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('cierra y marca como open=false cuando ocurre lostpointercapture tras arrastrar hacia abajo', async () => {
|
|
561
|
+
el.handle = true;
|
|
562
|
+
el.open = true;
|
|
563
|
+
await el.updateComplete;
|
|
564
|
+
|
|
565
|
+
const dialog = el.shadowRoot?.querySelector('dialog') as HTMLDialogElement;
|
|
566
|
+
const handle = el.shadowRoot?.querySelector('.handle') as HTMLElement;
|
|
567
|
+
|
|
568
|
+
handle.setPointerCapture = vi.fn();
|
|
569
|
+
handle.releasePointerCapture = vi.fn();
|
|
570
|
+
|
|
571
|
+
vi.spyOn(dialog, 'getBoundingClientRect').mockReturnValue({
|
|
572
|
+
height: 300,
|
|
573
|
+
width: 375,
|
|
574
|
+
x: 0,
|
|
575
|
+
y: 300,
|
|
576
|
+
top: 300,
|
|
577
|
+
bottom: 600,
|
|
578
|
+
left: 0,
|
|
579
|
+
right: 375,
|
|
580
|
+
toJSON: () => {}
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
const closeSpy = vi.fn();
|
|
584
|
+
el.addEventListener('close', closeSpy);
|
|
585
|
+
|
|
586
|
+
handle.dispatchEvent(
|
|
587
|
+
new PointerEvent('pointerdown', {
|
|
588
|
+
bubbles: true,
|
|
589
|
+
clientX: 100,
|
|
590
|
+
clientY: 400,
|
|
591
|
+
pointerId: 1
|
|
592
|
+
})
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
dialog.dispatchEvent(
|
|
596
|
+
new PointerEvent('pointermove', {
|
|
597
|
+
bubbles: true,
|
|
598
|
+
clientX: 100,
|
|
599
|
+
clientY: 520, // 120px
|
|
600
|
+
pointerId: 1
|
|
601
|
+
})
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
dialog.dispatchEvent(
|
|
605
|
+
new PointerEvent('lostpointercapture', {
|
|
606
|
+
bubbles: true,
|
|
607
|
+
clientX: 0,
|
|
608
|
+
clientY: 0,
|
|
609
|
+
pointerId: 1
|
|
610
|
+
})
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
dialog.dispatchEvent(new Event('transitionend'));
|
|
614
|
+
await el.updateComplete;
|
|
615
|
+
|
|
616
|
+
expect(closeSpy).toHaveBeenCalled();
|
|
617
|
+
expect(el.open).toBe(false);
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it('cierra y marca como open=false cuando se arrastra un bottom sheet expandido hacia abajo', async () => {
|
|
621
|
+
el.handle = true;
|
|
622
|
+
el.open = true;
|
|
623
|
+
await el.updateComplete;
|
|
624
|
+
|
|
625
|
+
const dialog = el.shadowRoot?.querySelector('dialog') as HTMLDialogElement;
|
|
626
|
+
const handle = el.shadowRoot?.querySelector('.handle') as HTMLElement;
|
|
627
|
+
dialog.classList.add('expanded');
|
|
628
|
+
|
|
629
|
+
handle.setPointerCapture = vi.fn();
|
|
630
|
+
handle.releasePointerCapture = vi.fn();
|
|
631
|
+
|
|
632
|
+
vi.spyOn(dialog, 'getBoundingClientRect').mockReturnValue({
|
|
633
|
+
height: 600,
|
|
634
|
+
width: 375,
|
|
635
|
+
x: 0,
|
|
636
|
+
y: 100,
|
|
637
|
+
top: 100,
|
|
638
|
+
bottom: 700,
|
|
639
|
+
left: 0,
|
|
640
|
+
right: 375,
|
|
641
|
+
toJSON: () => {}
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
const closeSpy = vi.fn();
|
|
645
|
+
el.addEventListener('close', closeSpy);
|
|
646
|
+
|
|
647
|
+
handle.dispatchEvent(
|
|
648
|
+
new PointerEvent('pointerdown', {
|
|
649
|
+
bubbles: true,
|
|
650
|
+
clientX: 100,
|
|
651
|
+
clientY: 100,
|
|
652
|
+
pointerId: 1
|
|
653
|
+
})
|
|
654
|
+
);
|
|
655
|
+
|
|
656
|
+
dialog.dispatchEvent(
|
|
657
|
+
new PointerEvent('pointermove', {
|
|
658
|
+
bubbles: true,
|
|
659
|
+
clientX: 100,
|
|
660
|
+
clientY: 550, // Delta de 450px hacia abajo (arrastrado fuera / muy abajo)
|
|
661
|
+
pointerId: 1
|
|
662
|
+
})
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
handle.dispatchEvent(
|
|
666
|
+
new PointerEvent('pointerup', {
|
|
667
|
+
bubbles: true,
|
|
668
|
+
clientX: 100,
|
|
669
|
+
clientY: 550,
|
|
670
|
+
pointerId: 1
|
|
671
|
+
})
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
dialog.dispatchEvent(new Event('transitionend'));
|
|
675
|
+
await el.updateComplete;
|
|
676
|
+
|
|
677
|
+
expect(closeSpy).toHaveBeenCalled();
|
|
678
|
+
expect(el.open).toBe(false);
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
it('no cierra el bottom sheet si el usuario arrastra hacia abajo pero lo sube de nuevo antes de soltar', async () => {
|
|
682
|
+
el.handle = true;
|
|
683
|
+
el.open = true;
|
|
684
|
+
await el.updateComplete;
|
|
685
|
+
|
|
686
|
+
const dialog = el.shadowRoot?.querySelector('dialog') as HTMLDialogElement;
|
|
687
|
+
const handle = el.shadowRoot?.querySelector('.handle') as HTMLElement;
|
|
688
|
+
|
|
689
|
+
handle.setPointerCapture = vi.fn();
|
|
690
|
+
handle.releasePointerCapture = vi.fn();
|
|
691
|
+
|
|
692
|
+
vi.spyOn(dialog, 'getBoundingClientRect').mockReturnValue({
|
|
693
|
+
height: 300,
|
|
694
|
+
width: 375,
|
|
695
|
+
x: 0,
|
|
696
|
+
y: 300,
|
|
697
|
+
top: 300,
|
|
698
|
+
bottom: 600,
|
|
699
|
+
left: 0,
|
|
700
|
+
right: 375,
|
|
701
|
+
toJSON: () => {}
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
const closeSpy = vi.fn();
|
|
705
|
+
el.addEventListener('close', closeSpy);
|
|
706
|
+
|
|
707
|
+
// Inicia arrastre en Y = 400
|
|
708
|
+
handle.dispatchEvent(
|
|
709
|
+
new PointerEvent('pointerdown', {
|
|
710
|
+
bubbles: true,
|
|
711
|
+
clientX: 100,
|
|
712
|
+
clientY: 400,
|
|
713
|
+
pointerId: 1
|
|
714
|
+
})
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
// Arrastra hacia abajo (Y = 600, delta = 200px)
|
|
718
|
+
dialog.dispatchEvent(
|
|
719
|
+
new PointerEvent('pointermove', {
|
|
720
|
+
bubbles: true,
|
|
721
|
+
clientX: 100,
|
|
722
|
+
clientY: 600,
|
|
723
|
+
pointerId: 1
|
|
724
|
+
})
|
|
725
|
+
);
|
|
726
|
+
|
|
727
|
+
// Cambia de opinión y lo sube de nuevo cerca del origen (Y = 410, delta = 10px)
|
|
728
|
+
dialog.dispatchEvent(
|
|
729
|
+
new PointerEvent('pointermove', {
|
|
730
|
+
bubbles: true,
|
|
731
|
+
clientX: 100,
|
|
732
|
+
clientY: 410,
|
|
733
|
+
pointerId: 1
|
|
734
|
+
})
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
// Suelta en Y = 410
|
|
738
|
+
handle.dispatchEvent(
|
|
739
|
+
new PointerEvent('pointerup', {
|
|
740
|
+
bubbles: true,
|
|
741
|
+
clientX: 100,
|
|
742
|
+
clientY: 410,
|
|
743
|
+
pointerId: 1
|
|
744
|
+
})
|
|
745
|
+
);
|
|
746
|
+
|
|
747
|
+
await el.updateComplete;
|
|
748
|
+
|
|
749
|
+
expect(closeSpy).not.toHaveBeenCalled();
|
|
750
|
+
expect(el.open).toBe(true);
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
it('permite abrir y cerrar mediante los métodos show() y close()', async () => {
|
|
754
|
+
expect(el.open).toBe(false);
|
|
755
|
+
el.show();
|
|
756
|
+
await el.updateComplete;
|
|
757
|
+
expect(el.open).toBe(true);
|
|
758
|
+
|
|
759
|
+
const dialog = el.shadowRoot?.querySelector('dialog') as HTMLDialogElement;
|
|
760
|
+
const closePromise = el.close();
|
|
761
|
+
dialog.dispatchEvent(new Event('transitionend'));
|
|
762
|
+
await closePromise;
|
|
763
|
+
await el.updateComplete;
|
|
764
|
+
expect(el.open).toBe(false);
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
it('abre el diálogo nativo como modal con showModal cuando modal=true', async () => {
|
|
768
|
+
await el.updateComplete;
|
|
769
|
+
const dialog = el.shadowRoot?.querySelector('dialog') as HTMLDialogElement;
|
|
770
|
+
const showModalSpy = vi.fn();
|
|
771
|
+
const showSpy = vi.fn();
|
|
772
|
+
(dialog as any).showModal = showModalSpy;
|
|
773
|
+
(dialog as any).show = showSpy;
|
|
774
|
+
|
|
775
|
+
expect(el.modal).toBe(true);
|
|
776
|
+
el.open = true;
|
|
777
|
+
await el.updateComplete;
|
|
778
|
+
|
|
779
|
+
expect(showModalSpy).toHaveBeenCalled();
|
|
780
|
+
expect(showSpy).not.toHaveBeenCalled();
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
it('abre el diálogo nativo con show() en vez de showModal cuando modal=false', async () => {
|
|
784
|
+
el.modal = false;
|
|
785
|
+
await el.updateComplete;
|
|
786
|
+
const dialog = el.shadowRoot?.querySelector('dialog') as HTMLDialogElement;
|
|
787
|
+
const showModalSpy = vi.fn();
|
|
788
|
+
const showSpy = vi.fn();
|
|
789
|
+
(dialog as any).showModal = showModalSpy;
|
|
790
|
+
(dialog as any).show = showSpy;
|
|
791
|
+
|
|
792
|
+
el.open = true;
|
|
793
|
+
await el.updateComplete;
|
|
794
|
+
|
|
795
|
+
expect(showSpy).toHaveBeenCalled();
|
|
796
|
+
expect(showModalSpy).not.toHaveBeenCalled();
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
it('cierra el diálogo nativo con close() al poner open=false', async () => {
|
|
800
|
+
await el.updateComplete;
|
|
801
|
+
const dialog = el.shadowRoot?.querySelector('dialog') as HTMLDialogElement;
|
|
802
|
+
(dialog as any).showModal = vi.fn(() => {
|
|
803
|
+
(dialog as any).open = true;
|
|
804
|
+
});
|
|
805
|
+
const closeSpy = vi.fn();
|
|
806
|
+
(dialog as any).close = closeSpy;
|
|
807
|
+
|
|
808
|
+
el.open = true;
|
|
809
|
+
await el.updateComplete;
|
|
810
|
+
el.open = false;
|
|
811
|
+
await el.updateComplete;
|
|
812
|
+
|
|
813
|
+
expect(closeSpy).toHaveBeenCalled();
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
it('aplica fallback al atributo open cuando el entorno no implementa showModal', async () => {
|
|
817
|
+
await el.updateComplete;
|
|
818
|
+
const dialog = el.shadowRoot?.querySelector('dialog') as HTMLDialogElement;
|
|
819
|
+
(dialog as any).showModal = undefined;
|
|
820
|
+
(dialog as any).show = undefined;
|
|
821
|
+
|
|
822
|
+
el.open = true;
|
|
823
|
+
await el.updateComplete;
|
|
824
|
+
|
|
825
|
+
expect(dialog.hasAttribute('open')).toBe(true);
|
|
826
|
+
expect((dialog as any).open).toBe(true);
|
|
827
|
+
});
|
|
435
828
|
});
|