From 58c49765c18753878b0420bee6f2dc5abf768d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=A1=D0=BA=D0=BE=D1=82=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2?= Date: Wed, 12 Aug 2026 16:12:31 +0700 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80?= =?UTF-8?q?=D0=B6=D0=BA=D0=B0=20=D0=B8=D0=B7=D0=BE=D0=B1=D1=80=D0=B0=D0=B6?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B9=20=D0=B2=20toggleButton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ToggleButton/ToggleButton.stories.ts | 7 +- src/components/ToggleButton/ToggleButton.tsx | 45 ++++++++++-- .../__tests__/ToggleButton.test.tsx | 23 +++++++ .../__snapshots__/ToggleButton.test.tsx.snap | 64 ++++++++++++++++++ src/components/ToggleButton/hotels.png | Bin 0 -> 10204 bytes 5 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 src/components/ToggleButton/hotels.png diff --git a/src/components/ToggleButton/ToggleButton.stories.ts b/src/components/ToggleButton/ToggleButton.stories.ts index c3192cf5..b593f401 100644 --- a/src/components/ToggleButton/ToggleButton.stories.ts +++ b/src/components/ToggleButton/ToggleButton.stories.ts @@ -3,7 +3,12 @@ import { IconArrowDownRight, IconUser } from '@tabler/icons-react-native' import { ToggleButton } from './ToggleButton' -const icons = { IconArrowDownRight, IconUser, none: undefined } +const icons = { + IconArrowDownRight, + IconUser, + hotels: require('./hotels.png'), + none: undefined, +} const meta: Meta = { title: 'Form/ToggleButton', diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index b8e0e140..c65e0492 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -1,6 +1,8 @@ import { memo, useCallback, useMemo, useState } from 'react' import { type AccessibilityProps, + Image, + type ImageSourcePropType, Pressable, type StyleProp, Text, @@ -43,8 +45,8 @@ export interface ToggleButtonProps size?: 'xlarge' | 'large' | 'base' | 'small' /** Дополнительная стилизация для контейнера компонента */ style?: StyleProp - /** SVG-иконка */ - Icon?: SvgSource + /** SVG-иконка или PNG / ImageSource */ + Icon?: SvgSource | ImageSourcePropType } /** @@ -75,11 +77,20 @@ export const ToggleButton = memo( ) const icon = Icon ? ( - + isSvgIcon(Icon) ? ( + + ) : ( + + ) ) : null const onPressIn = useCallback(() => setPressed(true), []) @@ -296,6 +307,26 @@ const toggleStyles = StyleSheet.create( }) ) +const isSvgIcon = ( + source: SvgSource | ImageSourcePropType +): source is SvgSource => { + if (typeof source === 'function') return true + + if (typeof source === 'number') return false + + if (Array.isArray(source)) return false + + if (source === null || typeof source !== 'object') return false + + if ('xml' in source) return true + + if ('uri' in source && typeof source.uri === 'string') { + return /\.svg(?$|[?#])/i.test(source.uri) + } + + return false +} + export const ToggleButtonTestId = { root: 'ToggleButton', container: 'ToggleButton.container', diff --git a/src/components/ToggleButton/__tests__/ToggleButton.test.tsx b/src/components/ToggleButton/__tests__/ToggleButton.test.tsx index 60f1df2a..14bc0e14 100644 --- a/src/components/ToggleButton/__tests__/ToggleButton.test.tsx +++ b/src/components/ToggleButton/__tests__/ToggleButton.test.tsx @@ -70,6 +70,16 @@ describe('ToggleButton', () => { iconPos: 'right', }, ], + [ + 'checked = false, disabled = false, iconOnly = true, size = base, with PNG Icon', + { + checked: false, + disabled: false, + iconOnly: true, + size: 'base', + Icon: require('../hotels.png'), + }, + ], ] beforeAll(() => { @@ -90,6 +100,19 @@ describe('ToggleButton', () => { }) }) + test('should render PNG Icon as Image', () => { + const { getByTestId } = render( + + ) + const icon = getByTestId(ToggleButtonTestId.icon) + + expect(icon.type).toBe('Image') + }) + test('should handle press', async () => { const mockedOnPress = jest.fn() const { queryByTestId } = render() diff --git a/src/components/ToggleButton/__tests__/__snapshots__/ToggleButton.test.tsx.snap b/src/components/ToggleButton/__tests__/__snapshots__/ToggleButton.test.tsx.snap index 213d712b..d99139b2 100644 --- a/src/components/ToggleButton/__tests__/__snapshots__/ToggleButton.test.tsx.snap +++ b/src/components/ToggleButton/__tests__/__snapshots__/ToggleButton.test.tsx.snap @@ -472,6 +472,70 @@ exports[`ToggleButton snapshots checked = false, disabled = false, iconOnly = tr `; +exports[`ToggleButton snapshots checked = false, disabled = false, iconOnly = true, size = base, with PNG Icon 1`] = ` + + + + + +`; + exports[`ToggleButton snapshots checked = false, disabled = true, iconOnly = false, size = large, with Icon, with label 1`] = ` =?j|L?tZ>)G|_>TbHB>83&6(6pmw zPzDr;Vq$cp$B;pTOa^sy4HM0ZFisd(j0T;MnB`0`8Arlei5V5pAZR2$@PPu62Ac-# zhHjzXuj)rt-S_{WGiSffe|uJDQ3YLIXRSV~Z`ZAR|L5Lk@BQs>@ADVHiE(0_7$?Sw zablbpC&r0!Vw@N!#))xaoERs@|M$bdFV8UYa`?;nSN`6>)b{;-v!%=pc2Y|l*oOD6 zO4nTLo6ddSZ}dC+)5gsV4+XivmRHxzcj zeT-qPx^N6@`19IKt5eVF8)r6t`QQKK-uFE&hjY;5@ynoyQh0UV{)y*rTXFKr4>qS7 z;5seeXy?!fn#jF}@Q*l>fN%HI2bR*xszn&awG@;&XN1uVh^dwPw z6NG7HinDMHDa3?nErO>V9ZmDpS~`v=rFeRugwOg*;Oa*X*`ZbIo_pXA`c8))E2HNY zvHlNt+_d)WWq({d+{~ZYb|{&etwFophAj9K)KZQ@&+{<5gP6n2w#JaqjS+i5$r^Xe zt$48r+Jg28%5YhDpFmowK&ez>rQ|KwuI;m{PoDhJU4OLp9nizkOGQ{%w=VhgZ9i$7 z{q6i)PaH`0P0s|kQHRFtQE25Iu(r$>=vB~sx$J3J5%@~1#(2X5#-M=l!x_kZ7LH@- z^O3<(7#J9UQc{9u*rwBNLDrgf;{)S%|MtIGy6N6e?1vr&?-Y7B-22~k_x0IS4!if) zk^?id&}_~^ZT1kfXAb$JM-GB7kC{pkT#}T7+EP*oMu!k2pj+^ka8vlt=j5u;J%vC+ zF@juFy8)&$0=As_p^?Owf{A$aL}>}KtZa`?@A8XBFZj=4;~SwzqnC<++)iB6XT9Hh zsA2M)XK1H(1X|O(VY2MDAK7yIm9yD_PT3746>z2%_Ni1LSWWm3TBwBJdCdP~2HW^~ z%gnfmv5V5tOFr_2M$4^Rv19<+nS))2^Pq?h_;$lUf8Q0*!_iYk)N0vat6egUwu4TF zaUdtRGch!1p8ooKR>@|*2`_)_+W)NC^|LR&@{ND&8mEoV_U+hL15|$Qati*=n+o)B zSm@z!F3U0aVm^>y5XJx<0&iOG-vaEbkEg$8EOltxrh#fXD1coXKW8C56wIY)?s|5TpEHNj-JMdotqy1vqOXB z&%I)q@#*1Brk2-W@6lbZue{<9VSz^d!3Tc-@9RhIx%f3}pfSAx1_C(fua1~QFHCmRj!0Penxgsz-MoxlfrH5aHNR05}oh+j~!hq>{aj55tFqBEq zJFp>_FlON|DV&lNftfY2vxz;S2qQ7Vh&4FB@G`+f4hL5Gh*mefw}_qxYfH5@QV6l8 z!9^`Na>*q)=fO2`k-PW7y61|(N;H~+55l1mr&;(Yk!LJ;v<8=lD=;Te6HsXYEg=tq zGee)fU~?l@mintzl03rLKs*LZ?7b#>ZV_3YlO}So;(f6*BXrN3C5RD zu5%o6u*suTs)ww(72dZ>U}uib_(MN@ZhQLP>)w?A?t6#rJ>Pl?4$qE2-Pg^QEl;~g z?)+BvA3kPYbH$t1ZQXF-&);{^;u}8;3&VH)^KyWN9kG+{)Jw1HtJ=k9FF*5jS5_z2 zzRn~AuUNKpbj8+fM=Fh4J4n%*AoLKkJ0>s>A!g(e(%L{nU@sE|bjNjQis^!OtThaK zsh5V&NIe|k7^pE!)?Ii#{^U3Pj{A3-Kl@4@{>NwE4NFe$fa&XpK!L+ZTKCUv{IS36 zvJLL`kDQxs+Fk#|udKM~BmdmJER>)YVo-Je6>s_E(CU@1zIOH6SKZKPSI?`}^Q1O? z1hRG;@^%NCj?gv1G)Ax)*gI{Zzx=;p!+#%xn?HXPzW?2~z~FcTnl2nC%!1r>VAHcrc*DhC%Hga}qb#qX?xq3UU1`_Lj1$}N1raX)=-3Ld@V2XNOno`t{qo8N}z zOKaYg2h2I=zIJ-cQ(t}kcW-|iTF7yWA;&`zB$qe8Z{wnK-*Dcx=boNjguM~Ymn^y+ z5AKkf^E2l z%s1ff`=5dD-1#$DzT^dX*9WhK$;)1|cc8^sOr3ju|>v9aLvizw>mEP>MSTE_lK)tnS`gFKE*xy$gn;2^oxFa&#lWZgqP{2UXCK{sn^|6 zUVr&@jTg2}=cWE?G8_~zQ1P^up%a9kqws@JFj;Q5Gcx&3_$#uzbE;#3@AoPK-?J!T4P&qWT7{f!k>|k)H0|92czy6<>+S%+a z4}S4ulb6BEKvXYP5v2WFzT~$)y=CUqN~K~J^;t*o^EA_2osbW- zI`nFWSDlp6fsV8}V$497<<&)D+{js|A&Cw`cI4-s3Rx<6l+K~`P_r@neB7P_zt``* z!~^EAykn)}B;dd=&C1yHUvjX0=K%cPd%ppXJ$?_g+7+0XSPG>OxF7uf3vkPa_QS?a z--TCRv;wLF1F_woB9q1DxRRMZn&o?rzWM4b$v-u{^I^WwUw|So{KES8_dm2}Pv(ZI zsU7P#&|i_8442Vths3Pefg1YN;F21xU=6`CI^q^`TJOL!IHwv!PM>=behku{bB&0F z4uQZ>0q5yDZJ26;eD3nR}YS^E)f>nUCHJ%SSc`lynm8Kd=|} z9oz}K_a1O>5QCKK+v<~mtcN3e0F~c(ev62r^ULmoAmB4G{h_#h%IfGy%hB1SfZH2n>4Z0L3 zpZgFarlwN({r5fu%f}uLlVvmzfN5|^gU~w@@S;!e+4fKy<~d#x`&t>F_>Tu?j2{fac_dgv zDdE|Of}U-3!tBRc4}e|X4J^BUx&47|zM(!h1_drP(Cw9)rA`%f8iphXPXbRw*fE5WE zTM#H}qHTmO z2_Uqel`TmUNMRw#h(sdVju8qGGw*P1|L|}>EE%8RATG!Z+j?xIR%AV%CiJXRf!HR} zDxJ;%oW17fFtv9dtUCFU%VD1GXPM(n_O_aqnU~CF-Xfqmu0c$Ue@Nx!h6GznkGVUX|uHi zF1TnL%+^cLKep{7JC=K(OvgKl^_pv`c)Hfse3U z2}sCYv`;Sx5Ob&1r`KS#e;dq{ZW~c(6a5 zM>Ajg z&-kzeBFIglxjOpJOJ1Vai`PGUv=TD#odG?L_ANR8o&B&-ifFZKtBshc5}hRM%+M>( zIdBuT;5kdS=(c=HrAo}OBipJ-3K)tSK}`@p*T5M3eSl>%ji6wRFP4#)6EK8<8UwNQ zK5|ub)N}wVu}}+n4en3wP8PmLFBT+P?X$3H^CTRt8;M{1R0AW&r?{h~54m2ipLr1= z@>}89nCz`ya|jj<&Vp$lSPb(R^HRj==e_b3?Y5LSF{{nl8c+d5B>?kBkyhZlrMd(% zm)Ps;a*np_Rm81G1g0DJT6ZS#(sx^E6+l5h5~4JeL;OaaY(>0 z6&zC#=)&kx8C}I6T5#@;?cCtf0sCZYOBCE0=9{>U*5^bYz>kLz*Oh0I#aku^B`e3` zhL3KCbCznKhetMyhy2Gtnv`OG!pD=@FGgZP&mVJm-bQ>Sx~CN@XW_KTojYM3V?K&7 z^+ulNUS}?dRS3q{BRPMJfbeE?7JDqM#XO?8hgh?LEk1 zL{VV!(M@_ihTafJ4Ly8TpXrB3H;vMRQ;OzrE$0f|A|RyT!Q=vkIp%A~p})TbE7v@C z0nB5}T@mZo+iF@)c~)={o@r+FnIQE557E;I&Srkdq7Xx_ zOA%m22{E9WXeCPiX(dgC$E&l{73RmfG3wk%U}VI>-QP|^oG1l^)#$P^pt?hm)M-Bz zE88S&g%Fz)0;p*ic>UWKUk&pZb5_K<#^sf83#n5#>AUI%(M?z}VR09-Z%#oD16xSt zgqrTkzvLo(43bfgYHDHsfIfu?j9&_Xlw{6#y>N1b1dtSHm=I)YoS%HgmLm_;k}K%1 zcHqZ9Sr&-BfbdEjXPsPwS~e)+7S{_xK6c*^zQ=VJI6jST>!sQ6QwvyE*ij1PzC46@IVRc#F>x`ntRzjKHZ=f0ejR^PqPGVcG7V0g-SAbZ=iU=!CkdVmMedUa` z?)6=ldVCbIxCSn@+8)FL$BqmYOwny&WWfo~s0{NApU8?JFG1Ny{9VM0B3OHYhEyuW zvL?00aPah6PL=&Z2tut{7>OZF%(^I0?d4G6^R{elN~;eJ8TiV5({ReNNvfyEllWr) z?rYYrLcJm{$EDqj$_V>IMitkRZ2!4`^oNy9%5n=wp4E-t-;sMGdRW0 zV#AQq86)n3h2}KqMdOtm!U%4c;~Hz@jdxE3Rehw^B}6A(8JLs!pxY7;%g{fb7GZS~ zCd%MgAAINDDGIhq{2gPaMxCf0j1b5%a_X`qf7)5&ubr!VnzJIRi~7epZ3)ieF0sI$ zd8xcnvc@in*#W5?Yk~*T z%*@V~R{ctI9n8T!%~=t%kaqAHtTtgmapy&N+hI3CN>B*e<)-w}Zj?jhN?r3sJ6}Ln z`MBswk{hSCL9qfwVzH*6NCtdtxAAcju~T;Q5_03 zxvZF}cvNOx<$@%G)nQ}?(%J-SM7FZnq-fGg)Twk`q7#KBhM!((B<>&`k%-$WpfkjC zsB$aRznOR{fQgGTDDpnSP^wiWU*uG;BD&?w3&kM(|Q;W~#C2!yo zZxMwk(;~FzOta?tKm75_Ukxwfp60BGyxDT05=yf-FMcTnS4;5CA5~%L_*kGlTD*@26FnZNhw@TKk+`9mzqqGE`={aDi%;SLdqrJV%t=reqGi<2)1SA2^DTcyZ{9aUk>mY+dO45GdnEjAu2@QlQM!k zODd3-EbQ5{3iclj<>B&xz*jGKq_IFPQSK03X}Gt(Y8i$`u zCzhHMtkMd=vzHgAh(f)M{z9J@j-4Y~7IjLkXq;h{ci9d-9EB|-On@}(vwz207@7P5 z%+BPr6;7UR6o(xqs5Fda?M@q}rw_v3?JvNtP)`2&3+KaW!)Lk=f9m#?Fo!PjIV+-B zZ;m6y%M~&fv<5~EnpV3p&{a7m7)*&tHFlXk-gtMwQ{fRatB8#7yDbC=Hm-#Oi6bCN z;MGJRiIgHT8{J>`Q1^Gtr-thi*2}}+!?pBPX5r^M&w(pWe;lS~4p83{3LIyB1-t6P zrbmw+f&IJo!VBA`VEgVxK>;hF*1kUEROi5+{RiOmwLdYxcEhjr-}hJd)!{|PoE1?{ zT&{VIqIHF`jO_(dh_A+1VmKpquHc9|f;1uoQS@OYi33|y-;hHJ8E@`%O@p3YU@kfhdFKgX8WG>d_5lU3@TnQVuR>0B0ZE^2w{n4LDT*WoCr-^l7)gL)rMZs?rj6?>P^g0@qC$pfLzWS zJ(C_&Xcl%)PC!7yO2oJ%xeF;%N~qHN!|u=t{0k>fN);%VLa_DtkoN}cIJ_D@_tlR> zQuffwN724O6TayHoO0?FLM^C%+%b)s_*}2Gf&Wgy}g!bwB zon>>+75PYWu_xsi3epPcSt!643A$#w>vN475qpEMnm#6u_@TmO3h$D;uuT*wikp}P zsv5mU$1YM`6%l3H7p_zZLkx}qB@Xo1gxoYxPTSBo;<>fQPUByD0Uwk_&I})A4Q+`r znm=GCOB+TfmmdD3o3FYK<`Agor3m%FHZrL4@>*-pVmpeuD*P@*SCLSYEM1efG!Y@| zyH@OaDR24+5J9>GNa*NNa_6JYK=tNKbXS^lFeO1gN&ch(PZZLD@=FRVw6E51*GP)M z!g6^xEhVfNPc7qN^AM!djOZM1g4Kjd9$%0rC`>#7H8GdNy>-+jy{HoAr3mV1IR>jOSJ$&P8>$0|J93n49A*Wwxt*$+zz=$ zS;9-T25rnU(yJ7+7LX1TwX%Z#V6cF(rYyF?!JKu4?@QFuj}~GAH)R#Qk=0qT4tea+ zJjR?8x&T2P`&bqbWudv!z(81E5t4+5$dWQIoQX?P>3~q+ng@u2pxfOPjc53*4glt? zobQ64bS?{VQM?x#L-GQ&7#|NTf1gNxr+bKC8kj6JoaiCsruE$c`EYGI*S1~$+qVCoWMC-MDT}Js-e7?Fw0e*TtIJgHp_V)XVX+(3^)+oMD8?* z+6XCQgIPB$^<5SsU#jbp98WBh%cmM2G?&3kp=M?1${Add>}h4h$JR@MbhxcC!bw@_ zV9eRAVk{6oiU4-P%YDJ8VsQxveX022l*#isd8GL(BFr*l#3HEBlM20!?uKTo6Dt65 z`W#QP_^dvieIN;1f&CTck}L2`e^Ww+e`F)&a*`ES7aM-%IKKOhKqD;6MpWh*HL13^ zfxydR7*&D+ZRzL7&|(nILAfPtCZJqM)`8>sOKCY?cS6`aDH`8^S0T-3%xmks>t<=` zl#^Z}DEU(~#HjdpiL;@UXK zS_Y6pyDOkBblrorQW_0TP}IF~8*DhU62vXJCL`_(d8snf?b1{0tC+t?QszVmPxdERy3xYz?AIx3TFMaS;}e_(5JmwNnn2fcDfnVH5oPJkvZZ zB0vEXvs#iQ3&(>zrh%1WbhZOXJG-D7p59U$?h+{YxcEeiEObkTxl}f?99p@PKXb<0 zJ4xJ>fz_Z(nY+{09<64px0G$HMi7V+qsU>?NPAFf7|1oc4x36KHna_9-Wv3^yz8yj zQpps7ABwaPipZeIABdCS@?$Q*UYor%XldZaK(6bmDG*99rr>AQ9t8H%OycRpw3D>D z;1kA#?WI}YwSdHe!e9l?P(WhJbc;aFw`+@d(&aj-u;pFsy<%%kK>rx0LFz+iJ0F}B zgf2?SOJ>+8LWB%Fo7dB7=B|hoV#gfIz{R4!LKK?Vf=*)#jMx5LsZH=?E0jYwX~d@r zEp0epf*>9rs06-wjWz6M@Ch_t*WuY>rIMF?Kz$TrdiCv=i$T%-nV1{V9H3gWC5oF7 z8d`oy*nw17S_2Rt>TjTMhT3?RK@KplATiGv!GK?{KQXq;?DdFJyXImxJ?_D4Z1KK} z*6sp2UY`rVGn+}2`diC#26D>T%^uiEH)9>R$yZgb7K6T zaG(Y8_f$~oUgfI$N&VZaHPLNLE<%r(2otK5&g+pubMNBt&S-Bc@2JHU4RYMy%0kv8 zN^=4aJi)o0#Y=YtZ)c(+Nq<&LMGmyd3~K?UG@)g~MY}woGZ*sCiHi8RTF({=Qj`-b zf!%>wyiBtHcrGN#oMie0a_D|W zAm=EqKw}Y96M&)`ws`JM71V z{%2ym(P6teE-^cO<_B9PewTc=2wrZh^>LE56)6GZb{CP6 z3R#wSiBYv1WXvNf#_eD2b~}eVymU6M|dVQ(Lk@sX#AR= zNzA>S#%I(FXXB2Xa5>LK6bA*WFdAlcy@V!60L2PYO$aqr_lkg|oGCL+pGXj)7YID1 zCBYK#jfwG+bw|-^WN+u9!jqUd@f~}0h29s2ATH5FUNP5vYhe^o zwwAq*bUElv>&-~I9ZjCHYUPY#5@)1-ECib@^f012ug8eb&d{XKQSF(gt Date: Wed, 12 Aug 2026 16:39:25 +0700 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=D1=80=D0=B0=D1=81=D1=88=D0=B8?= =?UTF-8?q?=D1=80=D0=B5=D0=BD=D0=BE=20=D0=BF=D0=BE=D0=BA=D1=80=D1=8B=D1=82?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BA=D0=B5=D0=B9=D1=81=D0=BE=D0=B2=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BF=D1=80=D0=B0=D0=B2=D0=B5=D1=80=D0=BA=D0=B8?= =?UTF-8?q?=20png/svg?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ToggleButton/ToggleButton.stories.ts | 21 ++++-- src/components/ToggleButton/ToggleButton.tsx | 53 +++++--------- .../__tests__/ToggleButton.test.tsx | 69 ++++++++++++++++++- 3 files changed, 98 insertions(+), 45 deletions(-) diff --git a/src/components/ToggleButton/ToggleButton.stories.ts b/src/components/ToggleButton/ToggleButton.stories.ts index b593f401..d7598359 100644 --- a/src/components/ToggleButton/ToggleButton.stories.ts +++ b/src/components/ToggleButton/ToggleButton.stories.ts @@ -3,12 +3,9 @@ import { IconArrowDownRight, IconUser } from '@tabler/icons-react-native' import { ToggleButton } from './ToggleButton' -const icons = { - IconArrowDownRight, - IconUser, - hotels: require('./hotels.png'), - none: undefined, -} +const svgIcons = { IconArrowDownRight, IconUser, none: undefined } + +const imageIcons = { hotels: require('./hotels.png'), none: undefined } const meta: Meta = { title: 'Form/ToggleButton', @@ -21,6 +18,7 @@ const meta: Meta = { label: 'ButtonToggle', size: 'base', Icon: IconArrowDownRight, + Image: undefined, }, argTypes: { iconPos: { @@ -29,7 +27,16 @@ const meta: Meta = { mapping: { null: null }, }, size: { control: 'radio', options: ['xlarge', 'large', 'base', 'small'] }, - Icon: { control: 'radio', options: Object.keys(icons), mapping: icons }, + Icon: { + control: 'radio', + options: Object.keys(svgIcons), + mapping: svgIcons, + }, + Image: { + control: 'radio', + options: Object.keys(imageIcons), + mapping: imageIcons, + }, onPress: { action: 'onPress' }, }, } diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index c65e0492..c4a14ddf 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -45,8 +45,10 @@ export interface ToggleButtonProps size?: 'xlarge' | 'large' | 'base' | 'small' /** Дополнительная стилизация для контейнера компонента */ style?: StyleProp - /** SVG-иконка или PNG / ImageSource */ - Icon?: SvgSource | ImageSourcePropType + /** SVG-иконка */ + Icon?: SvgSource + /** SVG-иконка */ + Image?: ImageSourcePropType } /** @@ -64,6 +66,7 @@ export const ToggleButton = memo( size = 'base', style, Icon, + Image: imageSource, testID, ...rest }) => { @@ -77,20 +80,18 @@ export const ToggleButton = memo( ) const icon = Icon ? ( - isSvgIcon(Icon) ? ( - - ) : ( - - ) + + ) : imageSource ? ( + ) : null const onPressIn = useCallback(() => setPressed(true), []) @@ -307,26 +308,6 @@ const toggleStyles = StyleSheet.create( }) ) -const isSvgIcon = ( - source: SvgSource | ImageSourcePropType -): source is SvgSource => { - if (typeof source === 'function') return true - - if (typeof source === 'number') return false - - if (Array.isArray(source)) return false - - if (source === null || typeof source !== 'object') return false - - if ('xml' in source) return true - - if ('uri' in source && typeof source.uri === 'string') { - return /\.svg(?$|[?#])/i.test(source.uri) - } - - return false -} - export const ToggleButtonTestId = { root: 'ToggleButton', container: 'ToggleButton.container', diff --git a/src/components/ToggleButton/__tests__/ToggleButton.test.tsx b/src/components/ToggleButton/__tests__/ToggleButton.test.tsx index 14bc0e14..a218e61a 100644 --- a/src/components/ToggleButton/__tests__/ToggleButton.test.tsx +++ b/src/components/ToggleButton/__tests__/ToggleButton.test.tsx @@ -77,7 +77,7 @@ describe('ToggleButton', () => { disabled: false, iconOnly: true, size: 'base', - Icon: require('../hotels.png'), + Image: require('../hotels.png'), }, ], ] @@ -105,7 +105,7 @@ describe('ToggleButton', () => { ) const icon = getByTestId(ToggleButtonTestId.icon) @@ -113,6 +113,71 @@ describe('ToggleButton', () => { expect(icon.type).toBe('Image') }) + test('should render SVG Icon as Svg (uri)', async () => { + const originalFetch = global.fetch + jest + .spyOn(global, 'fetch') + .mockImplementation() + .mockResolvedValue({ + ok: true, + status: 200, + text: async () => + '', + } as Response) + + try { + const { findByTestId } = render( + + ) + const icon = await findByTestId(ToggleButtonTestId.icon) + + expect(icon.type).toBe('RNSVGSvgView') + } finally { + global.fetch = originalFetch + } + }) + + test('should render SVG Icon as Svg (xml)', () => { + const { getByTestId } = render( + ', + }} + /> + ) + const icon = getByTestId(ToggleButtonTestId.icon) + + expect(icon.type).toBe('RNSVGSvgView') + }) + + test('should render non-svg uri Icon as Image', () => { + const { getByTestId } = render( + + ) + const icon = getByTestId(ToggleButtonTestId.icon) + + expect(icon.type).toBe('Image') + }) + + test('should render numeric Icon as Image', () => { + const { getByTestId } = render( + + ) + const icon = getByTestId(ToggleButtonTestId.icon) + + expect(icon.type).toBe('Image') + }) + test('should handle press', async () => { const mockedOnPress = jest.fn() const { queryByTestId } = render() From aa4b87cd023100db248631bdca61ebe7912db316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=A1=D0=BA=D0=BE=D1=82=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2?= Date: Thu, 13 Aug 2026 09:14:33 +0700 Subject: [PATCH 3/3] =?UTF-8?q?chore:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BF=D0=BE=D1=8F=D1=81=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BA=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D1=8F=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ToggleButton/ToggleButton.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx index c4a14ddf..5e5589d9 100644 --- a/src/components/ToggleButton/ToggleButton.tsx +++ b/src/components/ToggleButton/ToggleButton.tsx @@ -45,9 +45,9 @@ export interface ToggleButtonProps size?: 'xlarge' | 'large' | 'base' | 'small' /** Дополнительная стилизация для контейнера компонента */ style?: StyleProp - /** SVG-иконка */ + /** SVG-иконка. Если передана, то изображение не будет отображаться */ Icon?: SvgSource - /** SVG-иконка */ + /** Изображение. Будет отображаться, только если не передана иконка Icon */ Image?: ImageSourcePropType }