@@ -11,6 +11,7 @@ var customMatchers = require('../assets/custom_matchers');
11
11
var createGraphDiv = require ( '../assets/create_graph_div' ) ;
12
12
var destroyGraphDiv = require ( '../assets/destroy_graph_div' ) ;
13
13
var failTest = require ( '../assets/fail_test' ) ;
14
+ var drag = require ( '../assets/drag' ) ;
14
15
15
16
16
17
describe ( 'Test annotations' , function ( ) {
@@ -740,3 +741,233 @@ describe('annotation clicktoshow', function() {
740
741
. then ( done ) ;
741
742
} ) ;
742
743
} ) ;
744
+
745
+ describe ( 'annotation dragging' , function ( ) {
746
+ var gd ;
747
+
748
+ function textDrag ( ) { return gd . querySelector ( '.annotation-text-g>g' ) ; }
749
+ function arrowDrag ( ) { return gd . querySelector ( '.annotation-arrow-g>.anndrag' ) ; }
750
+ function textBox ( ) { return gd . querySelector ( '.annotation-text-g' ) ; }
751
+
752
+ beforeAll ( function ( ) {
753
+ jasmine . addMatchers ( customMatchers ) ;
754
+ } ) ;
755
+
756
+ beforeEach ( function ( done ) {
757
+ gd = createGraphDiv ( ) ;
758
+
759
+ // we've already tested autorange with relayout, so fix the geometry
760
+ // completely so we know exactly what we're dealing with
761
+ // plot area is 300x300, and covers data range 100x100
762
+ Plotly . plot ( gd ,
763
+ [ { x : [ 0 , 100 ] , y : [ 0 , 100 ] , mode : 'markers' } ] ,
764
+ {
765
+ xaxis : { range : [ 0 , 100 ] } ,
766
+ yaxis : { range : [ 0 , 100 ] } ,
767
+ width : 500 ,
768
+ height : 500 ,
769
+ margin : { l : 100 , r : 100 , t : 100 , b : 100 , pad : 0 }
770
+ } ,
771
+ { editable : true }
772
+ )
773
+ . then ( done ) ;
774
+ } ) ;
775
+
776
+ afterEach ( destroyGraphDiv ) ;
777
+
778
+ function initAnnotation ( annotation ) {
779
+ return Plotly . relayout ( gd , { annotations : [ annotation ] } )
780
+ . then ( function ( ) {
781
+ return Plots . previousPromises ( gd ) ;
782
+ } ) ;
783
+ }
784
+
785
+ function dragAndReplot ( node , dx , dy , edge ) {
786
+ return drag ( node , dx , dy , edge ) . then ( function ( ) {
787
+ return Plots . previousPromises ( gd ) ;
788
+ } ) ;
789
+ }
790
+
791
+ /*
792
+ * run through a series of drags of the same annotation
793
+ * findDragger: fn that returns the element to drag on
794
+ * (either textDrag or ArrowDrag)
795
+ * autoshiftX, autoshiftY: how much does the annotation anchor shift
796
+ * moving between one region and the next. Zero except if autoanchor
797
+ * is active, ie paper-referenced with no arrow
798
+ * coordScale: how big is the full plot? paper-referenced has scale 1
799
+ * and for the plot defined above, data-referenced has scale 100
800
+ */
801
+ function checkDragging ( findDragger , autoshiftX , autoshiftY , coordScale ) {
802
+ var bboxInitial = textBox ( ) . getBoundingClientRect ( ) ;
803
+ // first move it within the same auto-anchor zone
804
+ return dragAndReplot ( findDragger ( ) , 30 , - 30 )
805
+ . then ( function ( ) {
806
+ var bbox = textBox ( ) . getBoundingClientRect ( ) ;
807
+
808
+ // I'm not sure why these calculations aren't exact - they end up
809
+ // being off by a fraction of a pixel, or a full pixel sometimes
810
+ // even though as far as I can see in practice the positioning is
811
+ // exact. In any event, this precision is enough to ensure that
812
+ // anchor: auto is being used.
813
+ expect ( bbox . left ) . toBeWithin ( bboxInitial . left + 30 , 1 ) ;
814
+ expect ( bbox . top ) . toBeWithin ( bboxInitial . top - 30 , 1 ) ;
815
+
816
+ var ann = gd . layout . annotations [ 0 ] ;
817
+ expect ( ann . x ) . toBeWithin ( 0.1 * coordScale , 0.01 * coordScale ) ;
818
+ expect ( ann . y ) . toBeWithin ( 0.1 * coordScale , 0.01 * coordScale ) ;
819
+
820
+ // now move it to the center
821
+ // note that we explicitly offset by half the box size because the
822
+ // auto-anchor will move to the center
823
+ return dragAndReplot ( findDragger ( ) , 120 - autoshiftX , - 120 + autoshiftY ) ;
824
+ } )
825
+ . then ( function ( ) {
826
+ var bbox = textBox ( ) . getBoundingClientRect ( ) ;
827
+ expect ( bbox . left ) . toBeWithin ( bboxInitial . left + 150 - autoshiftX , 2 ) ;
828
+ expect ( bbox . top ) . toBeWithin ( bboxInitial . top - 150 + autoshiftY , 2 ) ;
829
+
830
+ var ann = gd . layout . annotations [ 0 ] ;
831
+ expect ( ann . x ) . toBeWithin ( 0.5 * coordScale , 0.01 * coordScale ) ;
832
+ expect ( ann . y ) . toBeWithin ( 0.5 * coordScale , 0.01 * coordScale ) ;
833
+
834
+ // next move it near the upper right corner, where the auto-anchor
835
+ // moves to the top right corner
836
+ // we don't move it all the way to the corner, so the annotation will
837
+ // still be entirely on the plot even with an arrow.
838
+ return dragAndReplot ( findDragger ( ) , 90 - autoshiftX , - 90 + autoshiftY ) ;
839
+ } )
840
+ . then ( function ( ) {
841
+ var bbox = textBox ( ) . getBoundingClientRect ( ) ;
842
+ expect ( bbox . left ) . toBeWithin ( bboxInitial . left + 240 - 2 * autoshiftX , 2 ) ;
843
+ expect ( bbox . top ) . toBeWithin ( bboxInitial . top - 240 + 2 * autoshiftY , 2 ) ;
844
+
845
+ var ann = gd . layout . annotations [ 0 ] ;
846
+ expect ( ann . x ) . toBeWithin ( 0.8 * coordScale , 0.01 * coordScale ) ;
847
+ expect ( ann . y ) . toBeWithin ( 0.8 * coordScale , 0.01 * coordScale ) ;
848
+
849
+ // finally move it back to 0, 0
850
+ return dragAndReplot ( findDragger ( ) , - 240 + 2 * autoshiftX , 240 - 2 * autoshiftY ) ;
851
+ } )
852
+ . then ( function ( ) {
853
+ var bbox = textBox ( ) . getBoundingClientRect ( ) ;
854
+ expect ( bbox . left ) . toBeWithin ( bboxInitial . left , 2 ) ;
855
+ expect ( bbox . top ) . toBeWithin ( bboxInitial . top , 2 ) ;
856
+
857
+ var ann = gd . layout . annotations [ 0 ] ;
858
+ expect ( ann . x ) . toBeWithin ( 0 * coordScale , 0.01 * coordScale ) ;
859
+ expect ( ann . y ) . toBeWithin ( 0 * coordScale , 0.01 * coordScale ) ;
860
+ } ) ;
861
+ }
862
+
863
+ // for annotations with arrows: check that dragging the text moves only
864
+ // ax and ay (and the textbox itself)
865
+ function checkTextDrag ( ) {
866
+ var ann = gd . layout . annotations [ 0 ] ,
867
+ x0 = ann . x ,
868
+ y0 = ann . y ,
869
+ ax0 = ann . ax ,
870
+ ay0 = ann . ay ;
871
+
872
+ var bboxInitial = textBox ( ) . getBoundingClientRect ( ) ;
873
+
874
+ return dragAndReplot ( textDrag ( ) , 50 , - 50 )
875
+ . then ( function ( ) {
876
+ var bbox = textBox ( ) . getBoundingClientRect ( ) ;
877
+ expect ( bbox . left ) . toBeWithin ( bboxInitial . left + 50 , 1 ) ;
878
+ expect ( bbox . top ) . toBeWithin ( bboxInitial . top - 50 , 1 ) ;
879
+
880
+ ann = gd . layout . annotations [ 0 ] ;
881
+
882
+ expect ( ann . x ) . toBe ( x0 ) ;
883
+ expect ( ann . y ) . toBe ( y0 ) ;
884
+ expect ( ann . ax ) . toBeWithin ( ax0 + 50 , 1 ) ;
885
+ expect ( ann . ay ) . toBeWithin ( ay0 - 50 , 1 ) ;
886
+ } ) ;
887
+ }
888
+
889
+ it ( 'respects anchor: auto when paper-referenced without arrow' , function ( done ) {
890
+ initAnnotation ( {
891
+ x : 0 ,
892
+ y : 0 ,
893
+ showarrow : false ,
894
+ text : 'blah<br>blah blah' ,
895
+ xref : 'paper' ,
896
+ yref : 'paper'
897
+ } )
898
+ . then ( function ( ) {
899
+ var bbox = textBox ( ) . getBoundingClientRect ( ) ;
900
+
901
+ return checkDragging ( textDrag , bbox . width / 2 , bbox . height / 2 , 1 ) ;
902
+ } )
903
+ . catch ( failTest )
904
+ . then ( done ) ;
905
+ } ) ;
906
+
907
+ it ( 'also works paper-referenced with explicit anchors and no arrow' , function ( done ) {
908
+ initAnnotation ( {
909
+ x : 0 ,
910
+ y : 0 ,
911
+ showarrow : false ,
912
+ text : 'blah<br>blah blah' ,
913
+ xref : 'paper' ,
914
+ yref : 'paper' ,
915
+ xanchor : 'left' ,
916
+ yanchor : 'top'
917
+ } )
918
+ . then ( function ( ) {
919
+ // with offsets 0, 0 because the anchor doesn't change now
920
+ return checkDragging ( textDrag , 0 , 0 , 1 ) ;
921
+ } )
922
+ . catch ( failTest )
923
+ . then ( done ) ;
924
+ } ) ;
925
+
926
+ it ( 'works paper-referenced with arrows' , function ( done ) {
927
+ initAnnotation ( {
928
+ x : 0 ,
929
+ y : 0 ,
930
+ text : 'blah<br>blah blah' ,
931
+ xref : 'paper' ,
932
+ yref : 'paper' ,
933
+ ax : 30 ,
934
+ ay : 30
935
+ } )
936
+ . then ( function ( ) {
937
+ return checkDragging ( arrowDrag , 0 , 0 , 1 ) ;
938
+ } )
939
+ . then ( checkTextDrag )
940
+ . catch ( failTest )
941
+ . then ( done ) ;
942
+ } ) ;
943
+
944
+ it ( 'works data-referenced with no arrow' , function ( done ) {
945
+ initAnnotation ( {
946
+ x : 0 ,
947
+ y : 0 ,
948
+ showarrow : false ,
949
+ text : 'blah<br>blah blah'
950
+ } )
951
+ . then ( function ( ) {
952
+ return checkDragging ( textDrag , 0 , 0 , 100 ) ;
953
+ } )
954
+ . catch ( failTest )
955
+ . then ( done ) ;
956
+ } ) ;
957
+
958
+ it ( 'works data-referenced with arrow' , function ( done ) {
959
+ initAnnotation ( {
960
+ x : 0 ,
961
+ y : 0 ,
962
+ text : 'blah<br>blah blah' ,
963
+ ax : 30 ,
964
+ ay : - 30
965
+ } )
966
+ . then ( function ( ) {
967
+ return checkDragging ( arrowDrag , 0 , 0 , 100 ) ;
968
+ } )
969
+ . then ( checkTextDrag )
970
+ . catch ( failTest )
971
+ . then ( done ) ;
972
+ } ) ;
973
+ } ) ;
0 commit comments