From 7da634e7727033bea4e88305fba30396c02bdbc3 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 19 Dec 2020 22:49:11 +0100 Subject: [PATCH 1/6] Fixed tags filtering from overview page --- src/servers/Overview.tsx | 10 ++++++++-- src/short-urls/ShortUrlsTable.tsx | 2 +- src/short-urls/helpers/ShortUrlsRow.tsx | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/servers/Overview.tsx b/src/servers/Overview.tsx index f6dd424e..03803036 100644 --- a/src/servers/Overview.tsx +++ b/src/servers/Overview.tsx @@ -1,6 +1,6 @@ import { FC, useEffect } from 'react'; import { Card, CardBody, CardHeader, CardText, CardTitle } from 'reactstrap'; -import { Link } from 'react-router-dom'; +import { Link, useHistory } from 'react-router-dom'; import { ShortUrlsListParams } from '../short-urls/reducers/shortUrlsListParams'; import { ShortUrlsList as ShortUrlsListState } from '../short-urls/reducers/shortUrlsList'; import { prettify } from '../utils/helpers/numbers'; @@ -40,6 +40,7 @@ export const Overview = ( const { loading: loadingTags } = tagsList; const { loading: loadingVisits, visitsCount } = visitsOverview; const serverId = isServerWithId(selectedServer) ? selectedServer.id : ''; + const history = useHistory(); useEffect(() => { listShortUrls({ itemsPerPage: 5, orderBy: { dateCreated: 'DESC' } }); @@ -95,7 +96,12 @@ export const Overview = ( See all » - + tags?.[0] && history.push(`/server/${serverId}/list-short-urls/1?tag=${tags[0]}`)} + /> diff --git a/src/short-urls/ShortUrlsTable.tsx b/src/short-urls/ShortUrlsTable.tsx index 55351166..4840ef6a 100644 --- a/src/short-urls/ShortUrlsTable.tsx +++ b/src/short-urls/ShortUrlsTable.tsx @@ -12,7 +12,7 @@ export interface ShortUrlsTableProps { renderOrderIcon?: (column: OrderableFields) => ReactNode; shortUrlsList: ShortUrlsListState; selectedServer: SelectedServer; - refreshList?: Function; + refreshList?: (params: ShortUrlsListParams) => void; shortUrlsListParams?: ShortUrlsListParams; className?: string; } diff --git a/src/short-urls/helpers/ShortUrlsRow.tsx b/src/short-urls/helpers/ShortUrlsRow.tsx index 7d7c6154..dde58c1e 100644 --- a/src/short-urls/helpers/ShortUrlsRow.tsx +++ b/src/short-urls/helpers/ShortUrlsRow.tsx @@ -16,7 +16,7 @@ import { ShortUrlsRowMenuProps } from './ShortUrlsRowMenu'; import './ShortUrlsRow.scss'; export interface ShortUrlsRowProps { - refreshList?: Function; + refreshList?: (params: ShortUrlsListParams) => void; shortUrlsListParams?: ShortUrlsListParams; selectedServer: SelectedServer; shortUrl: ShortUrl; From 23da0328ec7afcc3839f88d4f5932043ffe4759b Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 20 Dec 2020 08:56:46 +0100 Subject: [PATCH 2/6] Added Shlink logo as react component --- src/common/MainHeader.tsx | 4 ++-- src/common/img/ShlinkLogo.tsx | 23 +++++++++++++++++++++++ src/common/shlink-logo-white.png | Bin 8913 -> 0 bytes 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 src/common/img/ShlinkLogo.tsx delete mode 100644 src/common/shlink-logo-white.png diff --git a/src/common/MainHeader.tsx b/src/common/MainHeader.tsx index be06d9d0..4c213a7b 100644 --- a/src/common/MainHeader.tsx +++ b/src/common/MainHeader.tsx @@ -6,7 +6,7 @@ import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } f import classNames from 'classnames'; import { RouteComponentProps } from 'react-router'; import { useToggle } from '../utils/helpers/hooks'; -import shlinkLogo from './shlink-logo-white.png'; +import { ShlinkLogo } from './img/ShlinkLogo'; import './MainHeader.scss'; const MainHeader = (ServersDropdown: FC) => ({ location }: RouteComponentProps) => { @@ -21,7 +21,7 @@ const MainHeader = (ServersDropdown: FC) => ({ location }: RouteComponentProps) return ( - Shlink Shlink + Shlink diff --git a/src/common/img/ShlinkLogo.tsx b/src/common/img/ShlinkLogo.tsx new file mode 100644 index 00000000..757c3b22 --- /dev/null +++ b/src/common/img/ShlinkLogo.tsx @@ -0,0 +1,23 @@ +interface ShlinkLogoProps { + color?: string; + className?: string; +} + +export const ShlinkLogo = ({ color = '#4595e3', className }: ShlinkLogoProps) => ( + + + + + + + + +); diff --git a/src/common/shlink-logo-white.png b/src/common/shlink-logo-white.png deleted file mode 100644 index 5590667b3ca270a65edc4312a166544b2183de38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8913 zcmZ`3$wn_($+AfF8bpb1wIqbB)fQ1!7rnP&qa?9{)q@Se5~4<3Bm_aM z-ih{O)kKRD-{$-GJKuTFymQ_=cb>WPoH=vv%zIxxGS+3JX_=6bk&FH- zG*l!>aQG{KQblENpsPi8_3wGtR+>hV(0c1x_>z%vZ2l|cdoh%MNkVErsF61H5)B1C z1tZ!0?glclTVzlz^(R3yJ9&XkR%V~C)V!1mUy#{f|NJESkrpKC1yiOT<5TS>VQo#| z7k!RZlSBBySsD$kKp`p$_T${t{oUc+4>S}-@4z$^1Pa=+#q-_;U7>`uv@C0<FG4N0MxAfKQxzotVuK%Nw(h__m@ zSdsu9oRSp?fx~)Jq~hpU(D4RvQ$hpUNiX~3M6ed&0NrE2ErQf@;OgN_dy12Pb9e_D znSNNFxB`I<8>5IFmA@N*o`SFJDHWBT`4wT+@XhMrn1Q_hO_CL>PJLYmeY!bvU_3fXkFs#hQgX-c2=Qt#w zSq&_I5~1bLEG%G{DS=~}BWT+Ls>>`$desE-aEN3Hy@!7j_D*=0dvY04Z%WeZ zD0pdAykYV;eg-HtYu$^UQX~aV=aqz|14@`D2?9SDwTYQ?|r?370zR*lm;zadX6+U3aVU*<6Xwq9* zURdBxK&n zZuNyxA63K1lc^Ve7%khLo!LRJ$e0ADn-;b?W@!*YOFmE;l~zN?YX$_pB?lGH4j?|E zxo6&({mPiSgd%ujgDKOm7+V8!Dxm{SPzm^QwGDZ0KJ%MztFS{?QK-U}90RZAXB>K^ zfO4hsZs2aq-)4y#Cn|8BkN=^@lS5c#7@8gua+GPklSD59?Jf=ie~Qi=HRY)No5QKsfhS6c=B)o=1|j&e&%6z zpA#@Aj?^4>&I$;Dur4mxO}QEKUF{2ziXo`M+~VhZPz2$g(KJCR6gO~yu)Qr?6|D=|>+ zzCFx9?JsCLbh@%U=IC_Yw^y}Dy_*84+OxEBHROt^pDGDWL5vp{^&RK1xGbi1I>ES3 zYp{%W;56INiPEXN4~5pJ65nqO#g_6PNPy=omI&dj6zAH}0jM)4Z8T zD*!faS9r&;8vK(yXSY587yVQ+B}Dm_LZ5X%!v_^bjpU6F6KN&JGAiI#W%lB~pXi-X zMZj`iAvp$EId4!{x&9XG96^_u`{cg&Khw)g-t@O9ap`RCSr298%kZ-s@9LI)KTsuC zyT%zPWQs*x0wr$UMDargaWSRos$2|94DUR1YM%x?m5!q^-%`0?J8dyixRqd#4Jj+W zgEbwoihcICJhR;}1>n{feI1w-CF9ZcUK-H!j$(P48Y=HRnZhv__uZ{d$oX51;Qp-z z1{Kax{+}48xEBu58?LU6{`^0OcU4QSH{K5qySml~?Qq)U72CX!ryo}vsH&L<`A$m3 z(V90)FfQ@t_+;_&BXwry)+3Eh3m%(>4&u&+$W3KBw|F>#K4RVpMxPB|z43dj#{9uh zg6KQuyGyqu8pt8@Zk<(DL6D+xCfCU4u@h~;v06wv#+Rh^y4hJOsStC3y{hHc(EcR{y48kofyJ?>xVK2%jURCztp zEDsj^?1nK>!5!v=GkIf-W@L2@vTb67YAlVuZrxd0YOkKam;gkMSlj6h8!d9yW6@6R zt>C|7+%tAel6S!QyYy};cjN@zuUFaO!|^&}d;e7!`}k-X_OXCUZ2H9U60Q@++IVrG zk1w0)Z;bd+HMd509Nsvnz)mFW0Rb0Z?_tqu8D;7^b$nCIyu<-y-hB(s+(f=l;< z^m&%inX~>njYi?)OQ)%5!ZeO@sMoz(~M1%J>5-wstzLc70%U=VhalZi! z42_w8($($C`N)lod1}=4B|ZPo*ufAux+fKTUY3N|;RW7PVvsfaReeJ{Ip_u_^_;u! zy}`#~g5=8Wg7)>CQ!-#r!}IvPy0VOf@nM!z@V*q{h0!8wCYdhwb8iMaQx%=EWo*ND zA&X}3Vy}uaT*A0yH42p$B^EU;A8y*phnaTij#v;G+t9;Ro^?HN*!pRAwByf18^(HC z7cF9soSXVE5VR00F zxR#hSu7Pk%N-uk#P|xt%jk}rynn8ZD9i0UP>b`H_p}lyy`sU?bX}&2raBjf2vC(es zZ!7w?8`@u3myGbo_{J!;^W)Zd{&O@L+MTtk97a2J5A1oAo4(1-?1ltAJ&OP!C=nuP zz=su+J)*zB!=5v>o=D)S?wJhtR9({EtRwy!S}V}(nMwA`3Cu1VuJfAcWw>x3t;#^@ zf}O+V2H8igFLQ^HUbm)4U1oYU5E@!@Zl;)g7F@19hk(VSz!MKoo*bPol=+BzjwAJ@ zM4cCU#>(w|ji1G~-D{+es}m0zJjok=?}*1eg*MF2Axt?#VSm2TA#}saA99J39oN9P z=*@i!Z*LanrtMR07iF;m)lLY2l{eMAULsM%gVApAl(@>mASb4&q0T_%M=sC)nlrKZ3j%Z~dr-xuitfYY>6HqBeVL$XtbYJJ(UX-4qg z2tI}=u<^rYPXUW(u4q;%OYJWz@^Li@W`oy#gj&HMG>)F+&^yaRP8|ywjVix57*9Lgta6&;+EAXDz-xCdnJwStdW<8Pt6;2xCO{>dF z=(dD;&x%3t&%g+7m-6I|YATZhNT#wO*urXN(B11FoxC9Pn|xm#x9$8LNMorIhs0ec zDA2S4N$E8Re@hf5KH-XKZUizo)7y8Ftl71D(h#4DEh+O2YI4)E+G|0I!rJ()?vdXg z!_6c`4f6j~UsNYieeXs2-j4X#9!*UpNt6)X6EiL%zUfx0JmzTZc7i%h&lKlOcyZjk zOVLp7a%2M#)csQ5Zd$U-P)qwfY5Xt#TcWv_R39OZxMRUJ8v1kW^zSn$bH&W|RL4SP zL#=Qq7io+Xjr3hHD}cf^1$s`?16MB=VBng|??k*3J}25JDkYjRx(45m|0VEw!PYPm z1OQP)P|r~-Gqk<|*|)s7nIZ+Ce^LB;YZToG?17&%L}q8gtiebGybU8tj5-D$1diO z$X4E{ch-#kDtDt?8-46rn4$?W%JB2ZA^zck>KeeOyBrPU_a(w_-a+1ns+nA3gpiGb zvpi+DuDlY67d+6=QpH|TtDd@B=G6Ml5=SOm-!cZc50U@fdqTL<%eE|Rjtg^q6_S8iyY_OYfScj0K8BfJ$w!BlMkDdqrEa_}5e}X$Ei=bhs zgw76;8z<7Vz)J$JRdlw%m=zdwC<5KuB?w4kKnUo0>No_yI{3;Pg z^p_1(sua9r$^R8O?Q#LtkQyJDDaFft?n_X;s&>h$V!=i9KKG#cUUbf+ZcWn~UcIuy zvt3Qur(W&9I2UopJ1D!q7qfd%P0&u;pmZC_3vO>fp%%s6)bg>k0=?*zZFGPw6EBtM zr&32(wQ5NC1HvU<4nJdk*Stm407vjQcOX1+ea2CL8IF&`OG*Sw*3)vG4L+>AwhjFx zYiG!@SP(4WYkrR`HpCn$93)=3F>%9)+;SlJR-)!&4VZRqt1R7Ge^lLBk&=@LWkjTC}E(~RXTs8@` zSY_=d9O5O@Su5HgDuoO7=j9IW!d@O^9f-Vx@J z7+SVn`+l1ZVHA)aS%8(xIoi$!R>?{&r9-B+=%A`cWxbpx>V;dO_vuexgIf6R%ZGX* zy03W2TC1WyedLE$TxbEyl1CMF!S+5{yw`Fqsw^IEV~B z#59CFMeFE=)bw6+OAz(dK?=XU=k{M(cv<4?wt6J?+Wm-mmV^9mSDww>J^)@{Mc&!p zw@0y0#2a(-G}>K{gnEOS2FEL6b-1UzSCSS%|& za$LfaAQko*lQIAm?o9sybM{EP2;0(ht5C1=$7B?>5|`2uOQi#S`-M85%?9Yzc~?Y> zlgWK_C)Mk&x!MqMT8}$*)Fv~qzXhJ?lE)k-UH|wouO9ky7qumBAc^h>y|}LY(!t_Ypxe5BtMsbkptqut(Be7$`9D*pqk3?UT5RJVGR;1 z8jdd4D~HMW#7}=wr+r5T_r(G#9(BUo4Ln*+wJw@bJK~J17H0?7q4|~iBe&qAQ7rUK z%vUTz9Z4M#r@*S=&I?ehA{sl+O^H0g!+Xl+`-b5 z2YAxB$#edmw_yG{=VOlHBK5j9xs-^^`3kwVRkPHay|mRzRkGn2ge=HaF5xDMEM#FX{?$qcHsHi$$ zPJSff-TxEfyL*~1p5f;zv{zB~F=z*_@w~ApC={?=`yLBWoY_xNV*XAdtn5X^FHlkj z3FPDh%%urN$f@{|HOi7zFffww2i>rR{`=gj^W={rl*h8pkbOkyokmQ3sF{+F(j@vs z;j{zGa$3K`&v?1|EoSdX0+RAePu41!%aOYnI3-ky0S~O;GiF*Xr(D>Wn&Lw?eG7t5Z@sK%m>ak)-2^u5%@y z1c`61=QcDwMZcz(iSs`_amo#gfD>A_RCQBBSSp|)Sj0&7LVWY<48K{E{tit7hN2&4 zOQ2WWmeeIEyp}aZ1*W#!+7JTJRh~tZHV|YHcEsB0wTC`tQp9o5PLuPr*70p*4pu67 z;Nd7?bvmQILpS%1+Y(c{?CtO(1<2*J%)G2J)?jw-q&bqdNUkDzU$V_pW=;SZr`&Z1 znKxEE1exo>{M05k-OwUhK~pDZT|cap!bPtdD71(_d3Ua2vA|u&p11rqr5U&29Be>P zN#Xe%!`h-a@+_&hx7s{Me#xN@>89QY~Je8Zb?DPFQ%mn9P= zL)!SbxxTuJES*yHFM^tk30sx9WURp7(Ynvywj_pT00!Plz?~_D*n>6)YJ)=YV-!S5 zKF<7Q-`8uxzH!MmRriYl19igi4YE=W@4nvX#X4!4QKfArchg6;LsS7NC?RQ7=WoS+ zYt`7D*Hk+2I#F1O|Hv7`epYT~ixSwzMV{3_s3n2iR`^?mk^rMG$6c??!` z4E{kbhHjLNdq<;UsuNdcD;qhYZd%!Uj~A|-aQ&>pbGfrQ|2ZU=0uOEHhiy3CX;cX)`L-3E_$CQ|NBY2n`atGCnqjb zpwbd?UNy9AErvckW^jzC=p#ms=4VJvYZKp+Q;p}PT#c?CE#znlM9jMe$JTAAC<^_x zMc0|s`a?B;unDM4ZBde3tft0jNYH7pO-uFRbo&)jT~z+t^fB`=NWy2Ft{@cGu)sy4o zwKnQgD#XZE!pV(nr><^^_d-(6)3KQwPl~21<6{}^mUyD})~~1Nh@4 zhTHo2a!*7Yr>Ojdy?H8)0+@S*!1kW;-iPy~a820zCDx9KSqxSJ7;z2r2)I~h#rey; z&{(7tzx!eOggnz(?}*QAi)zDZ@W}~p<`k}AlE6?K%^&TxhWi_~ zw2#6}mb>VDp;)?z84OnNj_$=tLBPo8uUJAseUEpqL!Yi;?w`3X7S@Pm%%1*rfbiBF zi;<$K66MYr*_G%r^kF&n=BGp*r9EGp_~#!pWu={4&TZ5l$GC*641a#pgL%(9_wCOY zPo0U*={JSTiR}HMI)olBjgd|*;@cZTaTmDIOsO^0#MQ8rWI^iTnQ~ETe8Ql6SiTst z1?~Qb6nxl83}Ajz6Au)r*Ea!Ub|0`=v2SZ05hApM%wvP#z3if}-;m3MP>d9S$sFo6 zpeV_qHzWNTgFWJGf4lGf3LD#hy@FhexEiBUSqiD#$#*iWp*;SZOZht!t+TOe$#x>^keD-Y7N8#cyR~^E39jQb zi3{s2ER_1nNbng@P%n8eO6AWKeO8IOdZaF&mFG;0l3K3^eNCoNNi^8neSApI%)y38 zd)?f~WoiX$ay@%y%*1TV4=)hIgC4T^yegJjx1~H>iYa)Lr}pTBndwM2JWA!bvQ{j) zY-WiUve_J?O^j)FmeN!C0o=^4wJQ7+nkDs>X-H1svYzC*syOxe^^+EA~pX_g6|!5`Wp#Xo%s!K6K1~vCLZ3E1xG{0d~>cW zMTx5OnmxtQUl*bVJx?4AeT$B=NDir0scO`p27RAefBLIrMXER<9+%w-(%Jf{`MHK#xlFlF_Se)H4Bc3JxFv;F zHsS+TP8$$ecatVEqaLz_{V5g*XZ$dbc#31$NFw(Vlr5oLUe1ENNZ2qB@6*hV`$?^6 zJa)jFA+^JfJG?A|peD4Vb&e*(BIg!cK$t^F27fM*F#-< zRyiuu4Ct6eq<{s2ieX?~%OPQWkL~8Ho;Bq5y^^Y;qdpRiZsET0JkEEF9{0tZ?+dQt zdjDKj1y*VgU|i!9Y&TaEgPyK3(K!81az!g#;TD5Esw5gE4u~ruL?0+v(x z3rZGgZXdoVhV&%&w-Vko63e)ZYqhKWXKe^P&|s!6(n4`d>C;ka?jmx{s)sk*Y z3s;(LT|ews^#ejAlzY}SoI8R~9BKdRu$S^67kgkdAtW$(cS_vr`LK*&hf5yzIVJ~` zT@z4SDrx|?AgH1va(+MCAm?MML!|i{RQHBKyZetng?VOex{m0lk>}H01VX=lCkIjN zl7q-Vls4Dc$wbIjq8OtZ$#W>YL<^ZBDH^EqQ&5i6s{?o+$n?)`ltQ=4FyA(bW2ux!U9{-H|{&+?m4Hwn2=&HgTIfX?#y0#oBxdvN>?Gd@nYvtkQat(gc)%-FvYM%e+pY{L-!VnPe6_qlpJi$Rm)cL}qZ9BC=QY~Jd>;4@T z8>KB*b|!~E9z>b-lg?~R3WD9yDg8~=fPuqimvELT>Y(zjaci>g*90j)9*=iq$?JUa z#nav-ov@i`Q|?F@@=}N0oNBW-3k51roEpo3>)$r)(9BImOT={@eFbIIz##b(re&r6 z;C(CY@?%Xz4(il+h88DBJaWte|wmVZ&0}tJvUVnZ^vPILZnK>v>Ynuny;U#~K z_r;%^uDu!B61VCRT)*L_RP*c+?c(ILl{ z`Ev8zAKtpUExX#n*aT>j1LqII3LGoDw?etk0ue_{n7uw!kY?&nw@zunSlHnu6HWsy z??h2;cXnQm*F*Q6J4bhrcTnvDKuIV-taandkna?rJ%csrM{V3%JLmX_o6^%1h;&rY zbXzY*{5szJ>M22+(Gnq^Cj0yy5ekPAJlLjm>Jl_H970T&oZjI2TvuPpwqCI9EA)lU zT1M|gjo*E=Bg6kwbWz12X5rxRwr-T)SOZ6FNnL&XpuTt>)bW3jXLqS7(EvIuCpuH^Q5P?`Ni3=g;+`GS|#BQGYFeJ@>hWWP060+}W=$UNgmlQ(}8*|0d{BwCtUv8_lR^+`kE#7&yoN zAgwXyFGL{1S1It=PUu^Bbva-FGju@KN2{C^f^BnyS0|VGpg4gYf_?qd^vUSDI8u8E z{iLQqsm$=+uM#GH=W;F*^7!>iHOf6Io1I@J>q?9>Bv^o6rt-!0Db7=0j(bNQ=y(!& zWsM2rCubZ>gZPKdb!98i^-m-#{aywQzCx))*Z$WYTxo~jvY2$RAgLmPFm#hyc%G=m zz{kJi$%qO9vLypu-G`TIV0#Ay$I|)y59Q~g|5m;!^l>{n7W6YKWbTybJ7=VA55~eZN=y4YStU!h)_fXBKQyn2s89(W}P|nclD&XJ4J}}nfCh_#lPjy{laQF!8s}qB%YiuChvazGKJbK*tOj?O-Oi(Us2Xpb1h0|COI|{(o{e{!dbeVd@2+t404kodRbY QQt}BIRNGjqN&^w|KPs{e*Z=?k From fa949cde1286f746ac2fd6bcecb3fead83c9620d Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 20 Dec 2020 09:09:22 +0100 Subject: [PATCH 3/6] Simplified onTagClick handling in ShortUrlsTable --- src/servers/Overview.tsx | 2 +- src/short-urls/ShortUrlsList.tsx | 3 +-- src/short-urls/ShortUrlsTable.tsx | 11 ++++------- src/short-urls/helpers/ShortUrlsRow.tsx | 10 +++------- test/short-urls/helpers/ShortUrlsRow.test.tsx | 4 +--- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/servers/Overview.tsx b/src/servers/Overview.tsx index 03803036..892fe46e 100644 --- a/src/servers/Overview.tsx +++ b/src/servers/Overview.tsx @@ -100,7 +100,7 @@ export const Overview = ( shortUrlsList={shortUrlsList} selectedServer={selectedServer} className="mb-0" - refreshList={({ tags }) => tags?.[0] && history.push(`/server/${serverId}/list-short-urls/1?tag=${tags[0]}`)} + onTagClick={(tag) => history.push(`/server/${serverId}/list-short-urls/1?tag=${tag}`)} /> diff --git a/src/short-urls/ShortUrlsList.tsx b/src/short-urls/ShortUrlsList.tsx index 69287f81..485204dd 100644 --- a/src/short-urls/ShortUrlsList.tsx +++ b/src/short-urls/ShortUrlsList.tsx @@ -87,9 +87,8 @@ const ShortUrlsList = (ShortUrlsTable: FC) => boundToMercur orderByColumn={orderByColumn} renderOrderIcon={renderOrderIcon} selectedServer={selectedServer} - refreshList={refreshList} - shortUrlsListParams={shortUrlsListParams} shortUrlsList={shortUrlsList} + onTagClick={(tag) => refreshList({ tags: [ ...shortUrlsListParams.tags ?? [], tag ] })} /> ); diff --git a/src/short-urls/ShortUrlsTable.tsx b/src/short-urls/ShortUrlsTable.tsx index 4840ef6a..99e971a0 100644 --- a/src/short-urls/ShortUrlsTable.tsx +++ b/src/short-urls/ShortUrlsTable.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import { SelectedServer } from '../servers/data'; import { ShortUrlsList as ShortUrlsListState } from './reducers/shortUrlsList'; import { ShortUrlsRowProps } from './helpers/ShortUrlsRow'; -import { OrderableFields, ShortUrlsListParams } from './reducers/shortUrlsListParams'; +import { OrderableFields } from './reducers/shortUrlsListParams'; import './ShortUrlsTable.scss'; export interface ShortUrlsTableProps { @@ -12,8 +12,7 @@ export interface ShortUrlsTableProps { renderOrderIcon?: (column: OrderableFields) => ReactNode; shortUrlsList: ShortUrlsListState; selectedServer: SelectedServer; - refreshList?: (params: ShortUrlsListParams) => void; - shortUrlsListParams?: ShortUrlsListParams; + onTagClick?: (tag: string) => void; className?: string; } @@ -21,8 +20,7 @@ export const ShortUrlsTable = (ShortUrlsRow: FC) => ({ orderByColumn, renderOrderIcon, shortUrlsList, - refreshList, - shortUrlsListParams, + onTagClick, selectedServer, className, }: ShortUrlsTableProps) => { @@ -54,8 +52,7 @@ export const ShortUrlsTable = (ShortUrlsRow: FC) => ({ key={shortUrl.shortUrl} shortUrl={shortUrl} selectedServer={selectedServer} - refreshList={refreshList} - shortUrlsListParams={shortUrlsListParams} + onTagClick={onTagClick} /> )); }; diff --git a/src/short-urls/helpers/ShortUrlsRow.tsx b/src/short-urls/helpers/ShortUrlsRow.tsx index dde58c1e..1399099b 100644 --- a/src/short-urls/helpers/ShortUrlsRow.tsx +++ b/src/short-urls/helpers/ShortUrlsRow.tsx @@ -5,7 +5,6 @@ import { ExternalLink } from 'react-external-link'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCopy as copyIcon } from '@fortawesome/free-regular-svg-icons'; import CopyToClipboard from 'react-copy-to-clipboard'; -import { ShortUrlsListParams } from '../reducers/shortUrlsListParams'; import ColorGenerator from '../../utils/services/ColorGenerator'; import { StateFlagTimeout } from '../../utils/helpers/hooks'; import Tag from '../../tags/helpers/Tag'; @@ -16,8 +15,7 @@ import { ShortUrlsRowMenuProps } from './ShortUrlsRowMenu'; import './ShortUrlsRow.scss'; export interface ShortUrlsRowProps { - refreshList?: (params: ShortUrlsListParams) => void; - shortUrlsListParams?: ShortUrlsListParams; + onTagClick?: (tag: string) => void; selectedServer: SelectedServer; shortUrl: ShortUrl; } @@ -26,7 +24,7 @@ const ShortUrlsRow = ( ShortUrlsRowMenu: FC, colorGenerator: ColorGenerator, useStateFlagTimeout: StateFlagTimeout, -) => ({ shortUrl, selectedServer, refreshList, shortUrlsListParams }: ShortUrlsRowProps) => { +) => ({ shortUrl, selectedServer, onTagClick }: ShortUrlsRowProps) => { const [ copiedToClipboard, setCopiedToClipboard ] = useStateFlagTimeout(); const [ active, setActive ] = useStateFlagTimeout(false, 500); const isFirstRun = useRef(true); @@ -36,14 +34,12 @@ const ShortUrlsRow = ( return No tags; } - const selectedTags = shortUrlsListParams?.tags ?? []; - return tags.map((tag) => ( refreshList?.({ tags: [ ...selectedTags, tag ] })} + onClick={() => onTagClick?.(tag)} /> )); }; diff --git a/test/short-urls/helpers/ShortUrlsRow.test.tsx b/test/short-urls/helpers/ShortUrlsRow.test.tsx index e45640bc..c4f565a9 100644 --- a/test/short-urls/helpers/ShortUrlsRow.test.tsx +++ b/test/short-urls/helpers/ShortUrlsRow.test.tsx @@ -43,9 +43,7 @@ describe('', () => { beforeEach(() => { const ShortUrlsRow = createShortUrlsRow(ShortUrlsRowMenu, colorGenerator, useStateFlagTimeout); - wrapper = shallow( - , - ); + wrapper = shallow(); }); afterEach(() => wrapper.unmount()); From 260a6c49400e6104c7f79d24bac23e41577cbf66 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 20 Dec 2020 12:17:12 +0100 Subject: [PATCH 4/6] Improved welcome screen --- package-lock.json | 6 +-- package.json | 2 +- src/common/Home.scss | 35 +++++++++++++--- src/common/Home.tsx | 34 +++++++++++++--- src/common/img/ShlinkLogo.tsx | 6 ++- src/servers/ServersListGroup.scss | 25 +++++++++++- src/servers/ServersListGroup.tsx | 12 +++--- src/utils/theme/index.ts | 7 ++++ src/visits/helpers/DefaultChart.tsx | 9 +++-- src/visits/helpers/LineChartCard.tsx | 5 ++- test/common/Home.test.tsx | 26 +++++++----- test/common/img/ShlinkLogo.test.tsx | 34 ++++++++++++++++ test/servers/ServersListGroup.test.tsx | 49 ++++++++++++++++++----- test/visits/helpers/DefaultChart.test.tsx | 5 ++- 14 files changed, 203 insertions(+), 52 deletions(-) create mode 100644 src/utils/theme/index.ts create mode 100644 test/common/img/ShlinkLogo.test.tsx diff --git a/package-lock.json b/package-lock.json index 9c87279e..40bb319c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23075,9 +23075,9 @@ "dev": true }, "react-external-link": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/react-external-link/-/react-external-link-1.1.1.tgz", - "integrity": "sha512-e2WnTWkg81cuqxmDfjOalliAE20+Y/uD+lserN4uuwkwu+ciGLB3BMz4m7GnXh2+TowIi4sLtCL7zr7aDnIaqA==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-external-link/-/react-external-link-1.2.0.tgz", + "integrity": "sha512-6Lm0pD6rxrvZGVrWIWda809cAtrIlM3fDsKh9y5bKEVpOI0FTO2nTnxaqEood8+rw3svHJgpJGN6lZHO69ZTAQ==" }, "react-is": { "version": "16.7.0", diff --git a/package.json b/package.json index e4e1524d..95928585 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "react-copy-to-clipboard": "^5.0.2", "react-datepicker": "^3.3.0", "react-dom": "^17.0.1", - "react-external-link": "^1.1.1", + "react-external-link": "^1.2.0", "react-leaflet": "^3.0.2", "react-moment": "^1.0.0", "react-redux": "^7.2.2", diff --git a/src/common/Home.scss b/src/common/Home.scss index d82a49ce..775de88f 100644 --- a/src/common/Home.scss +++ b/src/common/Home.scss @@ -1,18 +1,41 @@ @import '../utils/base'; +@import '../utils/mixins/vertical-align'; .home { - text-align: center; - height: calc(100vh - #{$headerHeight} - #{($footer-height + $footer-margin)}); - display: flex; - align-items: center; - justify-content: center; - flex-flow: column; + position: relative; + padding-top: 15px; + + @media (min-width: $mdMin) { + padding-top: 0; + height: calc(100vh - #{$headerHeight} - #{($footer-height + $footer-margin)}); + } +} + +.home__logo { + @include vertical-align(); +} + +.home__main-card { + margin: 0 auto; + max-width: 720px; + + @media (min-width: $mdMin) { + @include vertical-align(); + } } .home__title { + text-align: center; font-size: 1.75rem; + margin: 0; @media (min-width: $mdMin) { font-size: 2.2rem; } } + +.home__servers-container { + @media (min-width: $mdMin) { + border-left: 1px solid rgba(0, 0, 0, .125); + } +} diff --git a/src/common/Home.tsx b/src/common/Home.tsx index 17e9e4b2..a72b1ee1 100644 --- a/src/common/Home.tsx +++ b/src/common/Home.tsx @@ -1,8 +1,11 @@ import { isEmpty, values } from 'ramda'; import { Link } from 'react-router-dom'; +import { Card, Row } from 'reactstrap'; +import { ExternalLink } from 'react-external-link'; import ServersListGroup from '../servers/ServersListGroup'; import './Home.scss'; import { ServersMap } from '../servers/data'; +import { ShlinkLogo } from './img/ShlinkLogo'; export interface HomeProps { servers: ServersMap; @@ -14,11 +17,32 @@ const Home = ({ servers }: HomeProps) => { return (
-

Welcome to Shlink

- - {hasServers && Please, select a server.} - {!hasServers && Please, add a server.} - + + +
+
+ +
+
+
+
+

Welcome!

+
+ + {!hasServers && ( +
+

This application will help you to manage your Shlink servers.

+

To start, please, add your first server.

+

+ You still don‘t have a Shlink server? + Learn how to get started. +

+
+ )} +
+
+
+
); }; diff --git a/src/common/img/ShlinkLogo.tsx b/src/common/img/ShlinkLogo.tsx index 757c3b22..40094a67 100644 --- a/src/common/img/ShlinkLogo.tsx +++ b/src/common/img/ShlinkLogo.tsx @@ -1,9 +1,11 @@ -interface ShlinkLogoProps { +import { MAIN_COLOR } from '../../utils/theme'; + +export interface ShlinkLogoProps { color?: string; className?: string; } -export const ShlinkLogo = ({ color = '#4595e3', className }: ShlinkLogoProps) => ( +export const ShlinkLogo = ({ color = MAIN_COLOR, className }: ShlinkLogoProps) => ( ( @@ -17,13 +19,13 @@ const ServerListItem = ({ id, name }: { id: string; name: string }) => ( ); -const ServersListGroup: FC = ({ servers, children }) => ( +const ServersListGroup: FC = ({ servers, children, embedded = false }) => ( <> -
-
{children}
-
+ {children &&
{children}
} {servers.length > 0 && ( - + {servers.map(({ id, name }) => )} )} diff --git a/src/utils/theme/index.ts b/src/utils/theme/index.ts new file mode 100644 index 00000000..a4b5f96b --- /dev/null +++ b/src/utils/theme/index.ts @@ -0,0 +1,7 @@ +export const MAIN_COLOR = '#4696e5'; + +export const MAIN_COLOR_ALPHA = 'rgba(70, 150, 229, 0.4)'; + +export const HIGHLIGHTED_COLOR = '#F77F28'; + +export const HIGHLIGHTED_COLOR_ALPHA = 'rgba(247, 127, 40, 0.4)'; diff --git a/src/visits/helpers/DefaultChart.tsx b/src/visits/helpers/DefaultChart.tsx index 0753ad35..78d1863a 100644 --- a/src/visits/helpers/DefaultChart.tsx +++ b/src/visits/helpers/DefaultChart.tsx @@ -7,6 +7,7 @@ import { fillTheGaps } from '../../utils/helpers/visits'; import { Stats } from '../types'; import { prettify } from '../../utils/helpers/numbers'; import { pointerOnHover, renderDoughnutChartLabel, renderNonDoughnutChartLabel } from '../../utils/helpers/charts'; +import { HIGHLIGHTED_COLOR, HIGHLIGHTED_COLOR_ALPHA, MAIN_COLOR, MAIN_COLOR_ALPHA } from '../../utils/theme'; import './DefaultChart.scss'; export interface DefaultChartProps { @@ -33,7 +34,7 @@ const generateGraphData = ( title, label: highlightedData ? 'Non-selected' : 'Visits', data, - backgroundColor: isBarChart ? 'rgba(70, 150, 229, 0.4)' : [ + backgroundColor: isBarChart ? MAIN_COLOR_ALPHA : [ '#97BBCD', '#F7464A', '#46BFBD', @@ -46,15 +47,15 @@ const generateGraphData = ( '#DCDCDC', '#463730', ], - borderColor: isBarChart ? 'rgba(70, 150, 229, 1)' : 'white', + borderColor: isBarChart ? MAIN_COLOR : 'white', borderWidth: 2, }, highlightedData && { title, label: highlightedLabel ?? 'Selected', data: highlightedData, - backgroundColor: 'rgba(247, 127, 40, 0.4)', - borderColor: '#F77F28', + backgroundColor: HIGHLIGHTED_COLOR_ALPHA, + borderColor: HIGHLIGHTED_COLOR, borderWidth: 2, }, ].filter(Boolean) as ChartDataSets[], diff --git a/src/visits/helpers/LineChartCard.tsx b/src/visits/helpers/LineChartCard.tsx index 588f50ea..28f23a0c 100644 --- a/src/visits/helpers/LineChartCard.tsx +++ b/src/visits/helpers/LineChartCard.tsx @@ -19,6 +19,7 @@ import { rangeOf } from '../../utils/utils'; import ToggleSwitch from '../../utils/ToggleSwitch'; import { prettify } from '../../utils/helpers/numbers'; import { pointerOnHover, renderNonDoughnutChartLabel } from '../../utils/helpers/charts'; +import { HIGHLIGHTED_COLOR, MAIN_COLOR } from '../../utils/theme'; import './LineChartCard.scss'; interface LineChartCardProps { @@ -173,8 +174,8 @@ const LineChartCard = ( const data: ChartData = { labels, datasets: [ - generateDataset(groupedVisits, 'Visits', '#4696e5'), - highlightedVisits.length > 0 && generateDataset(groupedHighlighted, highlightedLabel, '#F77F28'), + generateDataset(groupedVisits, 'Visits', MAIN_COLOR), + highlightedVisits.length > 0 && generateDataset(groupedHighlighted, highlightedLabel, HIGHLIGHTED_COLOR), ].filter(Boolean) as ChartDataSets[], }; const options: ChartOptions = { diff --git a/test/common/Home.test.tsx b/test/common/Home.test.tsx index 1f9c5a90..22a88cda 100644 --- a/test/common/Home.test.tsx +++ b/test/common/Home.test.tsx @@ -2,6 +2,7 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Mock } from 'ts-mockery'; import Home, { HomeProps } from '../../src/common/Home'; import { ServerWithId } from '../../src/servers/data'; +import { ShlinkLogo } from '../../src/common/img/ShlinkLogo'; describe('', () => { let wrapped: ShallowWrapper; @@ -19,21 +20,26 @@ describe('', () => { afterEach(() => wrapped?.unmount()); - it('shows link to create server when no servers exist', () => { + it('renders logo and title', () => { const wrapped = createComponent(); - expect(wrapped.find('Link')).toHaveLength(1); + expect(wrapped.find(ShlinkLogo)).toHaveLength(1); + expect(wrapped.find('.home__title')).toHaveLength(1); }); - it('asks to select a server when servers exist', () => { - const servers = { - '1a': Mock.of({ name: 'foo', id: '1' }), - '2b': Mock.of({ name: 'bar', id: '2' }), - }; + it.each([ + [ + { + '1a': Mock.of({ name: 'foo', id: '1' }), + '2b': Mock.of({ name: 'bar', id: '2' }), + }, + 0, + ], + [{}, 3 ], + ])('shows link to create or set-up server only when no servers exist', (servers, expectedParagraphs) => { const wrapped = createComponent({ servers }); - const span = wrapped.find('span'); + const p = wrapped.find('p'); - expect(span).toHaveLength(1); - expect(span.text()).toContain('Please, select a server.'); + expect(p).toHaveLength(expectedParagraphs); }); }); diff --git a/test/common/img/ShlinkLogo.test.tsx b/test/common/img/ShlinkLogo.test.tsx new file mode 100644 index 00000000..6e4b6494 --- /dev/null +++ b/test/common/img/ShlinkLogo.test.tsx @@ -0,0 +1,34 @@ +import { shallow, ShallowWrapper } from 'enzyme'; +import { ShlinkLogo, ShlinkLogoProps } from '../../../src/common/img/ShlinkLogo'; +import { MAIN_COLOR } from '../../../src/utils/theme'; + +describe('', () => { + let wrapper: ShallowWrapper; + const createWrapper = (props: ShlinkLogoProps) => { + wrapper = shallow(); + + return wrapper; + }; + + afterEach(() => wrapper?.unmount()); + + it.each([ + [ undefined, MAIN_COLOR ], + [ 'red', 'red' ], + [ 'white', 'white' ], + ])('renders expected color', (color, expectedColor) => { + const wrapper = createWrapper({ color }); + + expect(wrapper.find('g').prop('fill')).toEqual(expectedColor); + }); + + it.each([ + [ undefined, undefined ], + [ 'foo', 'foo' ], + [ 'bar', 'bar' ], + ])('renders expected class', (className, expectedClassName) => { + const wrapper = createWrapper({ className }); + + expect(wrapper.prop('className')).toEqual(expectedClassName); + }); +}); diff --git a/test/servers/ServersListGroup.test.tsx b/test/servers/ServersListGroup.test.tsx index bab7c52d..9a6ad591 100644 --- a/test/servers/ServersListGroup.test.tsx +++ b/test/servers/ServersListGroup.test.tsx @@ -5,31 +5,58 @@ import ServersListGroup from '../../src/servers/ServersListGroup'; import { ServerWithId } from '../../src/servers/data'; describe('', () => { + const servers = [ + Mock.of({ name: 'foo', id: '123' }), + Mock.of({ name: 'bar', id: '456' }), + ]; let wrapped: ShallowWrapper; - const createComponent = (servers: ServerWithId[]) => { - wrapped = shallow(The list of servers); + const createComponent = (params: { servers?: ServerWithId[]; withChildren?: boolean; embedded?: boolean }) => { + const { servers = [], withChildren = true, embedded } = params; + + wrapped = shallow( + + {withChildren ? 'The list of servers' : undefined} + , + ); return wrapped; }; afterEach(() => wrapped?.unmount()); - it('Renders title', () => { - const wrapped = createComponent([]); + it('renders title', () => { + const wrapped = createComponent({}); const title = wrapped.find('h5'); expect(title).toHaveLength(1); expect(title.text()).toEqual('The list of servers'); }); - it('shows servers list', () => { - const servers = [ - Mock.of({ name: 'foo', id: '123' }), - Mock.of({ name: 'bar', id: '456' }), - ]; - const wrapped = createComponent(servers); + it('does not render title when children is not provided', () => { + const wrapped = createComponent({ withChildren: false }); + const title = wrapped.find('h5'); - expect(wrapped.find(ListGroup)).toHaveLength(1); + expect(title).toHaveLength(0); + }); + + it.each([ + [ servers ], + [[]], + ])('shows servers list', (servers) => { + const wrapped = createComponent({ servers }); + + expect(wrapped.find(ListGroup)).toHaveLength(servers.length ? 1 : 0); expect(wrapped.find('ServerListItem')).toHaveLength(servers.length); }); + + it.each([ + [ true, 'servers-list__list-group servers-list__list-group--embedded' ], + [ false, 'servers-list__list-group' ], + [ undefined, 'servers-list__list-group' ], + ])('renders proper classes for embedded', (embedded, expectedClasses) => { + const wrapped = createComponent({ servers, embedded }); + const listGroup = wrapped.find(ListGroup); + + expect(listGroup.prop('className')).toEqual(expectedClasses); + }); }); diff --git a/test/visits/helpers/DefaultChart.test.tsx b/test/visits/helpers/DefaultChart.test.tsx index 9f0dee2b..49dd8069 100644 --- a/test/visits/helpers/DefaultChart.test.tsx +++ b/test/visits/helpers/DefaultChart.test.tsx @@ -3,6 +3,7 @@ import { Doughnut, HorizontalBar } from 'react-chartjs-2'; import { keys, values } from 'ramda'; import DefaultChart from '../../../src/visits/helpers/DefaultChart'; import { prettify } from '../../../src/utils/helpers/numbers'; +import { MAIN_COLOR, MAIN_COLOR_ALPHA } from '../../../src/utils/theme'; describe('', () => { let wrapper: ShallowWrapper; @@ -62,8 +63,8 @@ describe('', () => { const { datasets: [{ backgroundColor, borderColor }] } = horizontal.prop('data') as any; const { legend, legendCallback, scales } = horizontal.prop('options') ?? {}; - expect(backgroundColor).toEqual('rgba(70, 150, 229, 0.4)'); - expect(borderColor).toEqual('rgba(70, 150, 229, 1)'); + expect(backgroundColor).toEqual(MAIN_COLOR_ALPHA); + expect(borderColor).toEqual(MAIN_COLOR); expect(legend).toEqual({ display: false }); expect(legendCallback).toEqual(false); expect(scales).toEqual({ From 9e63c463ca6e92706e3b7aae284f6de2088a8372 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 20 Dec 2020 12:25:17 +0100 Subject: [PATCH 5/6] Styled scroll in servers list for home page --- src/servers/ServersListGroup.scss | 3 +++ src/utils/mixins/thin-scroll.scss | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/utils/mixins/thin-scroll.scss diff --git a/src/servers/ServersListGroup.scss b/src/servers/ServersListGroup.scss index 344bfd97..78457102 100644 --- a/src/servers/ServersListGroup.scss +++ b/src/servers/ServersListGroup.scss @@ -1,5 +1,6 @@ @import '../utils/base'; @import '../utils/mixins/vertical-align'; +@import '../utils/mixins/thin-scroll'; .servers-list__list-group.servers-list__list-group { width: 100%; @@ -33,6 +34,8 @@ @media (min-width: $mdMin) { max-height: 220px; overflow-x: auto; + + @include thin-scroll(); } .servers-list__server-item { diff --git a/src/utils/mixins/thin-scroll.scss b/src/utils/mixins/thin-scroll.scss new file mode 100644 index 00000000..ae243490 --- /dev/null +++ b/src/utils/mixins/thin-scroll.scss @@ -0,0 +1,16 @@ +@mixin thin-scroll() { + /* Forefox scrollbar */ + scrollbar-color: rgba(0, 0, 0, .2) #f5f5f5; + scrollbar-width: thin; + + /* Chrome webkit scrollbar */ + &::-webkit-scrollbar { + width: 6px; + background-color: #f5f5f5; + } + + &::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, .2); + border-radius: .5rem; + } +} From 42adbb3739d4dcc86c346b5a6744bf6a28aa5cbe Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 20 Dec 2020 12:32:54 +0100 Subject: [PATCH 6/6] Updated changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73f3bb1a..faf98cfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), * Date filtering can be now selected through relative times (last 7 days, last 30 days, etc) or absolute dates using date pickers. * Only the visits for last 30 days are loaded by default. You can change that at any moment if required. +* [#355](https://github.com/shlinkio/shlink-web-client/issues/355) Improved home page, fixing also its scrolling behavior for mobile devices. + ### Changed * [#267](https://github.com/shlinkio/shlink-web-client/issues/267) Added some subtle but important improvements on UI/UX. * [#352](https://github.com/shlinkio/shlink-web-client/issues/352) Moved from Scrutinizer to Codecov as the code coverage backend.