From e441f1d58c312f807940cbb70c69cb0ab137a937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Pretto?= <info@npretto.com> Date: Tue, 27 Aug 2024 18:09:52 +0200 Subject: [PATCH] poc of using loki to test png/pdf exports (#45650) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use loki to test png exports on questions * try to put the img in the body to see if it fixes the scrollbar issues in ci * fix linting issues * let's try again in ci * Attempt to make CI wait before taking a snapshot for downloaded PNGs * Use the snapshots from CI for PNG downloads * pdf export test thanks to kelvin suggestion about just testing the png * refactored the code to extract a util function * remove pdf tests as they broke in ci 🤷 * Update frontend/src/metabase/env.ts Co-authored-by: Phoomparin Mano <poom@metabase.com> * Update frontend/src/metabase/lib/loki-utils.ts --------- Co-authored-by: Mahatthana (Kelvin) Nomsawadi <me@bboykelvin.dev> Co-authored-by: Phoomparin Mano <poom@metabase.com> --- ...beddedQuestionView_Dark_Theme_Download.png | Bin 0 -> 10200 bytes ...eddedQuestionView_Light_Theme_Download.png | Bin 0 -> 10126 bytes .storybook/preview.tsx | 2 ++ frontend/src/metabase/{env.js => env.ts} | 7 +--- frontend/src/metabase/lib/loki-utils.ts | 25 +++++++++++++ .../PublicOrEmbeddedQuestionView.stories.tsx | 33 ++++++++++++++++++ .../visualizations/lib/save-chart-image.ts | 28 +++++++++------ 7 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 .loki/reference/chrome_laptop_embed_PublicOrEmbeddedQuestionView_Dark_Theme_Download.png create mode 100644 .loki/reference/chrome_laptop_embed_PublicOrEmbeddedQuestionView_Light_Theme_Download.png rename frontend/src/metabase/{env.js => env.ts} (67%) create mode 100644 frontend/src/metabase/lib/loki-utils.ts diff --git a/.loki/reference/chrome_laptop_embed_PublicOrEmbeddedQuestionView_Dark_Theme_Download.png b/.loki/reference/chrome_laptop_embed_PublicOrEmbeddedQuestionView_Dark_Theme_Download.png new file mode 100644 index 0000000000000000000000000000000000000000..08640a80c27dd2cafe8f65687c670945c1626a6e GIT binary patch literal 10200 zcmeHNc~Dcyx{u?63(L$n3WCTe0ml_&l{GS`h^(UmvX2T02mvKPfI#93Dgu&-5CTC5 zk$nw2gs7<O1X&GB!kWmw6GBLM=fqca@2z_E>ea1#Z@oV}Rn$40?sR|sb$?&~e!o7C ztu0L?cFOLAKp+yP7tUUSK(=0iKz?xAz72S@cTS}axNSmRGWi{n*Sdck_-701chf7| zfh%;o+an0%SBU9Zqbos4Qv<=zSpi|Q^F3}w&ziSDcZ6#`aDH$+r>LRk{_jRVKBz1? z<9v1J^R)6ikG60BtK2#6g+qpu!!KuJkDhx_Aa?Xe#?Nhs4j#V?RV5(9iF^|(YV6Gf zX`0*Atbc%WOL>t;#c}v#0!j@==I}|L#OkgU>L$P%$RJ&|eQGQC1X<f<v<JMuykISQ z@!QW=L{*+_u>oJ-IrJ|n_VA!cdlcuE^>dFSB&PRw$M+u_kP~Yui+us2l8G?hCek zTrJGw^Sn{k{<zJoDpzvw3gfALu=(DTP6?^7+rPUcY3DPtMb-)4z6@^Km>a64bVT=Y zK9R6QMVOy?Yt`Iv^)&LBJ1Mu!d*vqB@EO=C59R{Rf4qJF!d)qDSk3U)&!+h3)>dWY z7O;Ft`#xK;3s(FQei_y5iaQ+xn<w>SIRh;&Vqo#t!`oWqvo?BWs-~V3Hdekghps;k zooJ?~S5;N5_mglT(`pF4S=1dJ6uX(Ij%ZB+9)$J%{ST=+W1l`Y>2HHHQ5Mmz2S?7A z97nV4#5(#HysZV7vZl+z><lrVGKqowWZv*r*VgvDJa|4}(dOQ^&)IJ8W8*b25|AJZ za`pNbf7*H|rRPr_O^+FCq_1z&<2u}-UD78TozYhGXg%+EXz)TNBiNgrqwFiPc}eWP zaz<X^+JwF5$2|ln4n2H3j00Q$9J5t4qc7dswse^&kcb0Zg93&ZI7*1%%QLu&=&8+h zdzR?o+`XgV{Y>mjaFs)>p&37b_g{8=F9zK3-7Vjj0~ikfAEM-Q8(d`$?tw(MT#MNM zTu};#!*V+>>#XfQydHy+PDWcQTlJ3GAPy&mhQRfgZYg$)65m?Irk}3Bz!X%_ojCsW z>nZ_-M5d~tx<`0xWi@^1l@Go+{@!mHMf{h|hcYay1g-t+O^%Btkt(9f$l&$Ita3%s z?h)j??X0zc5v3i+?(OR>YVF<aH^x0_;ppgy)tN309rI#cw6p7RtUd@jP4!3dM=u?g zhpU6fr<@WdHv}A_Zg9sGwa$lVz#k9Y$jUaO(|GAUCsCz~hGGf-omRp8Iup~};{1hl z*~h86`39swziOdq)~!F|#n=pg;?G1!a)~+fCZ7pcnTPXMdQ>d&ur;$xVOznqT0eoN z-B@(hOasD@`>-rT69SQW9v<zYv^%=`HIM_}dM%8O)xF`5aY)o{O_>K<Tn2MBZgysP zQ<h)YqoTUh;mwV__Z&llvE<t9u)*!=9iE<^4gJzC9t$tP5uL%E`jxqWn=n1>HkA-Z z>CGA;KshNW+XKlu$Fx~$CsJUGmw6l<kHt%GqNQ|qckf^9QD_kIXOe9`MYXJNRT-O5 zSkGwa+YB})_;_b~R!mD`+~?_O;(S|e7=n^QCP;Hynz(u8YFyZ9Mbiat3eTyu4?dYw z!cMd=_|wRTDrl`<YBy_fkOQ0eiU6l$s01cVjR~85a*QurZ#<Si+TAzf@!=v^;Vnet zB`#c);op)>|D8MkzZF(|Z~WhY)xJCRpK)r&E2HIsK3|f6%b2u5WJfEcbXXd`iv5PK zs!tlr1-vqr)Kzu#Hq@L(BKPU|xv_X_lldh1GM1L>di%#H2NC=p4Dh^aJpi9Pq3+p% zhSxtk7+iN=u6m*nGt0M0?FK^^5;P#Y_llM7^kv@QPZ93Jy}K8v$MUB1W+x^ZlMrNN zr>v+}zGLmZoA{b%x}mFcPuK333Gz>e__I~6w$+PJAjE|dzO8Fzr8G%ER4*D=mP=1d z8?%X-u9>|X<@C)GO)n4Yfg3%}YM>=+)D7Zc9QR5{2u3bA80_JW_G@6sab!ES$$8dj zu~kY}eUk_xRv!u1Sh&7%yF-OCm=>=V(D+(>UwfOGD7RH#lb{+0<Qt%y!YA|NRgy7* zAt52{d@)}hYtleW+fTHa-08`AWfZ`ST=Zk*&-jiiiKc_8At8}PD|dIm-fk*B@A5Nf z!k|S*+sn!UEk{HZveboLT~eaSabAr(pC__oE>Tk>Bag<cWMJq6t^wU09fWg{d$mW` za&vQark>sR8y%XZ&yquqn&5^XggZ%tEy~Eq%3jG|71~rRWM%UWU4zHU#%giAyZr&& zrEsSW3L+_ShgU8!IyySsNlNsCtKeYsmzLZ-?d(`Mx*<2zr)Ie{bUlIQQ@@-(fJH<L zSLcb^IIMA=2%U=`2R$(H+?3(nI9fwDc`6f|9QfsAZG@C>{W30a#6qG%5^Pvi6|mlt z=}*{y`^&+6UB^m)&l+Ab&U;igzpn*G4J1;MxjJv1?z6+i72@KQXMr7iNKwiWK2y|` z@5F8fiAXcii`{=uEYFJG{;+9)YlC9TYHFje`C3*Ko}#I5f-sNUxvV5VmFp_%4|A0_ zM>SDiG&mwzeHoD@Wo7RE7z~R84vbmN8=siC*}2mc?~4-*L+8yh*NCrD?HV2^s(7x* zLgo_ngL#GHSRN0azsX`mb3a57+jDFE2r$l))KqmvljvsGYV?MAQh}|mt{!UfM28<g z8LjOn0S@y{PueS^5^C;=cqf1Crzm*>lgTVDE_UllMmO8ltmHbO1rolzWh;a62T$cG zezuMQq4?2tRBL0ZI%2H7Nys}v(*oOR+W_1(Q!r4vueUcbMjm|$ZXi5+K1wz&CUkDI z&Qh3|7Bs_9%ggqBSsCJ!u@pDC1sl@ph;;^k`{$n?9@g^e@G*VWkshMX<UG_Afk5QZ zhBypd3RTumpy0wE33n1rpG*Qc-*kPxw^oX-lA~|QIc915Mf)33U8t|`#Ne&!bgjP) z3=M*&=+HsIY?E~Bnm^0l*VngZHBII3#~v`=p0^*}?#b|-HK5GsdZhy{qg5}L+6l}r zgT~z%voXFuk?55}PKU#iae{Z6rufHRR`rW0j+UP&Q1A5QUV|AgXT)GtSpv4wK20FC zFzTX(*-mRMW@^~=KDiQ5mVmsS?0)vLa^}Nhouxrqy-NDP3o?^jvxwslmF&~G=@u>g z8X;Twt)^W2vzNL+;2xizXguf23)!pU(vHT`m-v3mQn=?W+NAbJ0E7ZA=CklEU3+Y0 zVx!~rirgCT?gubK52Y#ZAN>?{RTS`p5}p^V!&WLd0E9~LE)a&~>-w4&)du#Bj_c4h zu76tt+HM|-C;fT`Iyydoj$Ned9D@nRX&kgwOKV1~;^K^aY0Gm{65El*i#VSnN(OV` z8qkbD|3J1nB88-GX>cS$A><Z|cPzgeui%7n&Z}Wp{(Vj5hWqN+WI!SC*J7;7+q=iz zwlDVh;{bNFu==#GORBACC_%<RS|z-$s?q}>R%&d#2qgz~XpF{R67?y*<vSw=9raxa z_^yBe%iy0?K#WP$w<Pxt3ntN&<ee0}$mU3wnK$8;%w7-~H_Dug0Pf@(5Liq1X-3iM z$j(G?$(?~|{d#Z05Hmw2SgyLk>14v~kxy_Im0Q2#a0;2sWZJdJ7i<`UOl|Q?a~;3c zhZu-S?Ga_iwM>BL#+;sd1}1l%`RD3I{5(BQF~O?7^9TV201RB06F^fBVEy5i8%y>r z_P>BVe(hNk&_T70o|PR<SBz;j$JfNjA<Kno6lCi8G$2XvXG%;Yb-upMsrWP@F(AS% zUs-Dc`K;8wK0Gv18epHt9g0;=+lBl^SZ8<l3nZ(=rrXtbpc<LBXE9dlK<sWS1H~Bq zwW}k0MM-xp7priks<Mgz1@KA!+Lx{_YV*`XX;*x_o-T?`<+|Tr?rGAdg<NA+v6y2o zLl&*_o!Q=;FiF*(*E*1|Rxh}-+3aSj4GJZ_!hS9%t+FTsIZn^3p3>W=o#kG^uhC6l zzd)QIl=aqd=`km0;o|%7lX*g{PmR07$<+lVhT8~w^q4gE6vuU`YkleyX^nkeT!9?W zO-ul}CyH*6GE^HrcDTnWu5YIN7y>JdwCGEz{67Ezfl#uGOeXgS);TkL`q1uNzCv@J z-gLspNNG+U<_)4n)0WOk@w*Q0xD~t?p1k%71c!)7Y2oHq>N@(=*gH5l_;Hu`i2@~X zZXklQw2ibYrVCNNu8Dw<8ApiTaO-wJEhnLv=j;mx)1JzlIwa>)fc+FLPXLtO(3xGF z^s+K-9Jj2_eKC`;Xcg~DOkvJZ;Q1m5x3n8H5K4sMj4b(`#HJ*e@Pls=zea2&49i`@ zpnyNQ@_k^(s?Wq9>3x&OWsSa*X3!=7+GB$6)bpRAp5MLpzj{r!cWSFF@_*QI5~(5< z*=8l4^|F#quHwAjZAfHLIBxyrg$(qpf#3Uv^mHI|{O!ysq-o=)2zk61d|g*Tcj5+b zL5Wqbr;RmvV@d3vDgoo+4)!{5yt1;gl2bXcs=x5H89L4{{~SK4jXD7qzf{<#VHDtx zam2gL4}Ubp1B6uBv=@gHQNB5jcw)+{-mZaUs3#H`)>W66H`BhzWrp=61-8%ow)jEb zzFlU{YGD9m?Q$%CC~+oI8mpw;A9R`0v#KDG_;PJHauGz~Pv!KKO^X`KZ=2%NIr^7F zgc91!;(no$h!#hk^0;@?%F4>8Y9!CQY`Ut5FOY)wBnLJRe~d)+7<7=RA_1YzsyN#9 z#(QFm%U^#SSYI|~1f@nTNY4(yYQsy}f8}EpDF<+WF}y^c7o2Sx%`Kfpu88~z5xo0{ z)1>=vyB&SVIDR~?(Fz-615!9j&nDmuQs&h4AfIYsVp7oh;Y8|A$cBQ^o<DyKqGS1P z5BJ#CH>BfqQLaISj7h+8XK0$mj9rkq?%wf%J_I0ov;%ZLxAe<#3Idj#mzU>V$awBu z{%*~_IOz6F4Tq_3z#-Ofa!MywtG%*MsQZoX1c{wFZPw0WeJ2F6t&lk?FNaLXBpe>2 zkwewc{Q*`iW0F-eK1$082JGGqxn%>f6)M%whH4|m3XCON1yaoyFc`<m%1UrUAgykN zi0kK$^d#JZQ{o|mx$42~$_(#`(Wf$ohv@WVlB<ELVjXTcVgK@HDR+S4c~|f{_Xn@? z0eoT~9MYQPO&w!btlmSZ3#YSRQ>g|Dx(jnTHETC3rdRYxf;0$3T-G?El>MkyvH_5< z)!f^a?QsJE3pMv8>+b<_@#i|YsEj3;E(FDeQb01=@7K1eVu-U53+%N?=vI4sWY@gT zoB`i`=>5YA4infu#~|XrF&Cs%-#T*%atoug@N^1A-L-PnYoE4{_sSP!{Cb~MUQ0S% z_ppNA%xnL^KsKm-KpXy05LmNSf{rM_griCG3<_#Gpa&7-jBfo<a#(}AYZFA~L1k4H z3J4d@7lI|AXYVRv8Df-8LHQfN3DrrMck!rk_l=}Ic21`*AGW#(@L;@KNtbQQH5~7A zr~nsMMx5L%egU+tKBuhp19U@hNZ<6uN$13kkOed-fw^5UGgHnpIEc2QsSFGZnBwc; z24tu`=Dh=N8fU<G8n+3uE!PCmdeIjTtOot%&Z!&<&`!fdc5Y&=l+Nk&q#!3iyTU^O zp=B1s+$`04!32S*<!+S`?|-#Zwch`@Qv%e}%WG24%gf6QC;}=|JU$N>mu=AMdT}t$ zRvpNhk&-CcE-oil(ZqfJuaEl%m}Dzq%OAnRWwe&N5eH%;Yu2LN$z^Wi?dS7$sAiNK z@A>l?kG@e2Q=u+hlKg^8X---W>UKt4?{-9A$KIMwsOj;CK*R#UZvkQmQ1GAtGh5*p z$QU0WE;g$(a4hP=`$!e|r>7B#Ri`0uU$*0+jGK`yMU3YS`wS`#Ce4$ck$fvo7)MB2 zn3<V5I66`qfz0KbZ-9qpKb0fB$`xuW4Cb<39qt&tK@Gi65)vtk^EZS?U8!@`{=lKS z7j}sd%2lAnLwTRdR4nlOx$`&Fu77x>zci<)!|&hKKL!XZXMlC6Z2D#ZYbOa?8Z__q zC+M%&-{7^voL&SkayU<pZD@+g&yk({h0=cWJvGE@07$H_m1=n!almJR4cYAakGbJ) z$}1rL{!=tk0=WVtJ#oodAfM+{F968g*;(Y=3d~xVefh0xc(>^tUSsXP&UoB9b5F%^ zBM!;V(wRtAXy&uf){^`|SNEBI>BP!i5J*$}H+gIdK%p|LHG<_x%F2V1g{35f8lbUO zhsE6nkF!uJt9~<;h>&n81@dxtasr2(LXvg~D$J~^0cz~i3p9tfh8zNxu*JYuK-@8# zT-8)gVti|pNntU;gQZvn9bH&CyRu(xdS$A-HV`02$tVGhl>qn_NY!X_K%OFFW_VMy zSthZ3<$=GyzZ;Fpy@<hJ;4p!|7R&o$U<g+dIMr(gVi6Bj440yx)T8S4c~jkc`}!6; zrx>PqFnLx@7oiRWFBMJ|PM##{23#VC&F}!$@|&qzU~|G$&15U%I^_uX8Nnhj%J#lK zQj}KjuOtg%5)f(Tc6MlxLrj-4boIQsxZWy$LRR+a=n;iej<-~zN#VC*5C36iamJio z>jWQvpkbmqclXSW+}|+QJ1)r)_YM_o+v}F-Xj$u*tER5b$x8dn<DC8jxYCc(rg?)3 z+wO6h!nfAS9V~zUx)7SM63q#v*KpT)3<Hk2wa2YzvU1ZmJ&|#*TMbExiNEOGs0@Fw zCA;#Ctz<;)3BEY650gK};(%pVelEZww{&mV>t+PG^YxC;Ss6K(8rOCFPVYZ@EdNUL z5`S!bJk!2VeN?;NZf|6aAwRV2kmC6!mhUtC&%+ujn$D6D#=(<*oiv*LN0aEIrwlFm zE4b7$CL^Q%(JdvV51t-ZGqy>}yd0{1ufcyObK>grgOMLiNIQG7TW#|%cWFL}!CcrA zXVd7AoLhL~(DUbW1%bHbl9Nsz9g|)~8aF>U>pJ<F*G4=%5_av0n(Ng}0!_eVaHfcb z3rp1&MtAQAa5H{tw2$Y^oB&AT8L3}E)^VpVDJw4)%3}^?@3aZnhpKoD$)>SYoO1e3 z?A<duc60QRUYzhlM<Pikzqr#)LP6`wW--aOwgT(i5KR-Y$e4_p8l&-D3ZGvf`;-Fu zeFDDTdb~0<kdBCV^ME(|+vnPDy()0iA{C=q*swxG{tr3AWOTKQ=kTgW)Z^`T*mv8e z`<K&>8yX(%T_CfSl)lFGbh}J+WEIz?;9G3-Egnv4YTY!CnUx3J-+%uMNR53)rjak8 zCB^Fl(?K^<sUtyQW_9yxS-WYCV}1>`I0yXb*jU=TO}3_&x}Uf^-`u=e42>0L7MtU* zT)vFSY`vTYi_gxfzGSAh*K0m?*A*9DWMJs}xQ&7M4vAQ(JnZ%*d}n8W&iN|mcSd*P z`Ne%Ub_fDgU~iXR_(yCw7cc-q-E|i{?d*s5b{@gMZZCS`Q@25l=p6G4;5%PJd`YbE zPP^FDSk-B@A7+X7|9pFXqO#l8pv=GGcjJ4SA)OaKwkEvMRLQr#<mV8t*_txD9n%6Q zKr>^Yg!uR=gbFNatbBOrXnaR-vMZoz-eES^4uIO`;KoR|S&WH+YQU2hDq$|%0X=(` z!s*vpBZqy0TJSeu_2d3GS8}e}3k1c`kyB&vd|Mh;kY}$Q+<q2$ygr`c*8Xl-ajme} z{J?<&6Bskr$wkS%3bu}pRX>l6Xh1d2qJG{zI*sfuW675fkpV2$hMAYL0s37>G%FKq zk2gK<81E#iNA;G5MU-OahAeV<b(uGF!xUXJ8&D;|wS8F>#+B6>IH32tQu0Q>o;t_c zoo1s#{f*FICLw_vZ(08#*`Tk}ot!HyF0w^<{o(Lr=Yn5X=u8rm<?z}naHcY|2JBUD zd53~_Lx8{&=vs6|uZz@Qz!OGWzR6{P%>JWj3czxiv){fd0v@;h^zBQM|8UhMsiteJ WJ&QXAfp~&IOwU=K&HMe&d;bOTN2cTe literal 0 HcmV?d00001 diff --git a/.loki/reference/chrome_laptop_embed_PublicOrEmbeddedQuestionView_Light_Theme_Download.png b/.loki/reference/chrome_laptop_embed_PublicOrEmbeddedQuestionView_Light_Theme_Download.png new file mode 100644 index 0000000000000000000000000000000000000000..49c15193c2893324c7d891488d98062987d76190 GIT binary patch literal 10126 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 auKe8+gPr>zVg<so5B#F_4;A0L-TxObK$H&v literal 0 HcmV?d00001 diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index cff1963cd1a..cd48e8781ad 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -11,6 +11,7 @@ import { getMetabaseCssVariables } from "metabase/styled-components/theme/css-va import { css, Global, useTheme } from "@emotion/react"; import { baseStyle, rootStyle } from "metabase/css/core/base.styled"; import { defaultFontFiles } from "metabase/css/core/fonts.styled"; +import { saveDomImageStyles } from "metabase/visualizations/lib/save-chart-image"; export const parameters = { actions: { argTypesRegex: "^on[A-Z].*" }, @@ -30,6 +31,7 @@ const globalStyles = css` ${rootStyle} } + ${saveDomImageStyles} ${baseStyle} `; diff --git a/frontend/src/metabase/env.js b/frontend/src/metabase/env.ts similarity index 67% rename from frontend/src/metabase/env.js rename to frontend/src/metabase/env.ts index 92a9eda0162..5ad8833c1ca 100644 --- a/frontend/src/metabase/env.js +++ b/frontend/src/metabase/env.ts @@ -1,20 +1,15 @@ +// @ts-expect-error window.Cypress is not typed export const isCypressActive = !!window.Cypress; -// eslint-disable-next-line no-undef export const isStorybookActive = !!process.env.STORYBOOK; -// eslint-disable-next-line no-undef export const isProduction = process.env.WEBPACK_BUNDLE === "production"; -// eslint-disable-next-line no-undef export const isTest = process.env.NODE_ENV === "test"; -// eslint-disable-next-line no-undef export const shouldLogAnalytics = process.env.MB_LOG_ANALYTICS === "true"; export const isChartsDebugLoggingEnabled = - // eslint-disable-next-line no-undef process.env.MB_LOG_CHARTS_DEBUG === "true"; -// eslint-disable-next-line no-undef export const isEmbeddingSdk = !!process.env.IS_EMBEDDING_SDK_BUILD; diff --git a/frontend/src/metabase/lib/loki-utils.ts b/frontend/src/metabase/lib/loki-utils.ts new file mode 100644 index 00000000000..e26c360da66 --- /dev/null +++ b/frontend/src/metabase/lib/loki-utils.ts @@ -0,0 +1,25 @@ +export const openImageBlobOnStorybook = ({ + canvas, + blob, +}: { + canvas: HTMLCanvasElement; + blob: Blob; +}) => { + const imgElement = document.createElement("img"); + imgElement.src = URL.createObjectURL(blob); + // scale to /2 to compensate `scale:2` in html2canvas + imgElement.width = canvas.width / 2; + imgElement.height = canvas.height / 2; + + const root: HTMLElement = document.querySelector("#root")!; + const imageDownloaded = document.createElement("div"); + imageDownloaded.setAttribute("data-testid", "image-downloaded"); + root.replaceChildren(imgElement); + + // the presence of this element is used to detect when the image is ready + // in the storybook you'll need to `await canvas.findByTestId("image-downloaded");` + // and then call `asyncCallback()` to continue the story + root.appendChild(imageDownloaded); + + window.document.body.style.height = "initial"; +}; diff --git a/frontend/src/metabase/public/containers/PublicOrEmbeddedQuestion/PublicOrEmbeddedQuestionView.stories.tsx b/frontend/src/metabase/public/containers/PublicOrEmbeddedQuestion/PublicOrEmbeddedQuestionView.stories.tsx index 21744e18fb5..2b8faa1608d 100644 --- a/frontend/src/metabase/public/containers/PublicOrEmbeddedQuestion/PublicOrEmbeddedQuestionView.stories.tsx +++ b/frontend/src/metabase/public/containers/PublicOrEmbeddedQuestion/PublicOrEmbeddedQuestionView.stories.tsx @@ -136,6 +136,16 @@ LightThemeDefaultNoResults.args = { result: createMockDataset(), }; +export const LightThemeDownload = Template.bind({}); +LightThemeDownload.args = { + ...LightThemeDefault.args, + downloadsEnabled: true, +}; +LightThemeDownload.play = async ({ canvasElement }) => { + const asyncCallback = createAsyncCallback(); + await downloadQuestionAsPng(canvasElement, asyncCallback); +}; + // Dark theme export const DarkThemeDefault = Template.bind({}); DarkThemeDefault.args = { @@ -150,6 +160,13 @@ DarkThemeDefaultNoResults.args = { result: createMockDataset(), }; +export const DarkThemeDownload = Template.bind({}); +DarkThemeDownload.args = { + ...DarkThemeDefault.args, + downloadsEnabled: true, +}; +DarkThemeDownload.play = LightThemeDownload.play; + // Transparent theme export const TransparentThemeDefault = Template.bind({}); TransparentThemeDefault.args = { @@ -308,3 +325,19 @@ function NarrowContainer(Story: Story) { </Box> ); } + +const downloadQuestionAsPng = async ( + canvasElement: HTMLElement, + asyncCallback: () => void, +) => { + const canvas = within(canvasElement); + + const downloadButton = await canvas.findByTestId("download-button"); + await userEvent.click(downloadButton!); + + const documentElement = within(document.documentElement); + const pngButton = await documentElement.findByText(".png"); + await userEvent.click(pngButton); + await canvas.findByTestId("image-downloaded"); + asyncCallback(); +}; diff --git a/frontend/src/metabase/visualizations/lib/save-chart-image.ts b/frontend/src/metabase/visualizations/lib/save-chart-image.ts index 489fc6a0227..d5f8bcc0706 100644 --- a/frontend/src/metabase/visualizations/lib/save-chart-image.ts +++ b/frontend/src/metabase/visualizations/lib/save-chart-image.ts @@ -1,12 +1,14 @@ import { css } from "@emotion/react"; +import { isStorybookActive } from "metabase/env"; +import { openImageBlobOnStorybook } from "metabase/lib/loki-utils"; +import EmbedFrameS from "metabase/public/components/EmbedFrame/EmbedFrame.module.css"; + export const SAVING_DOM_IMAGE_CLASS = "saving-dom-image"; export const SAVING_DOM_IMAGE_HIDDEN_CLASS = "saving-dom-image-hidden"; export const SAVING_DOM_IMAGE_DISPLAY_NONE_CLASS = "saving-dom-image-display-none"; -import EmbedFrameS from "metabase/public/components/EmbedFrame/EmbedFrame.module.css"; - export const saveDomImageStyles = css` .${SAVING_DOM_IMAGE_CLASS} { .${SAVING_DOM_IMAGE_HIDDEN_CLASS} { @@ -41,14 +43,20 @@ export const saveChartImage = async (selector: string, fileName: string) => { canvas.toBlob(blob => { if (blob) { - const link = document.createElement("a"); - const url = URL.createObjectURL(blob); - link.rel = "noopener"; - link.download = fileName; - link.href = url; - link.click(); - link.remove(); - setTimeout(() => URL.revokeObjectURL(url), 60_000); + if (isStorybookActive) { + // if we're running storybook we open the image in place + // so we can test the export result with loki + openImageBlobOnStorybook({ canvas, blob }); + } else { + const link = document.createElement("a"); + const url = URL.createObjectURL(blob); + link.rel = "noopener"; + link.download = fileName; + link.href = url; + link.click(); + link.remove(); + setTimeout(() => URL.revokeObjectURL(url), 60_000); + } } }); }; -- GitLab