From cadab1587120547052c22adf7c2961827ae089ba Mon Sep 17 00:00:00 2001 From: Pavel <14926950+prichodko@users.noreply.github.com> Date: Thu, 29 Jun 2023 17:48:48 +0100 Subject: [PATCH] [website] Add Jobs page (#433) * add jobs route * implement jobs page * imlement jobs detail page * remove vertical padding from feature list * fix field * fix button pressing * fix feature list * fix tag --------- Co-authored-by: Jakub Kotula <520927+jkbktl@users.noreply.github.com> --- apps/website/public/assets/jobs/codex.png | Bin 0 -> 3388 bytes apps/website/public/assets/jobs/logos.png | Bin 0 -> 2146 bytes apps/website/public/assets/jobs/nimbus.png | Bin 0 -> 1234 bytes apps/website/public/assets/jobs/vac.png | Bin 0 -> 1830 bytes apps/website/public/assets/jobs/waku.png | Bin 0 -> 2146 bytes apps/website/src/components/feature-list.tsx | 12 +- apps/website/src/config/routes.ts | 2 +- .../src/pages/features/create-community.tsx | 12 +- apps/website/src/pages/jobs/[slug].tsx | 154 ++++++++++ apps/website/src/pages/jobs/index.tsx | 276 ++++++++++++++++++ apps/website/src/pages/keycard.tsx | 12 +- apps/website/src/styles/global.css | 3 + packages/components/src/button/button.tsx | 8 +- .../src/dynamic-button/dynamic-button.tsx | 8 +- .../src/hooks/use-pressable-colors.tsx | 17 +- .../src/icon-button/icon-button.tsx | 8 +- packages/components/src/input/input.tsx | 1 - .../src/react-button/react-button.tsx | 8 +- packages/components/src/tabs/tabs.tsx | 8 +- packages/components/src/tag/tag.tsx | 17 +- packages/components/src/types.ts | 12 +- 21 files changed, 492 insertions(+), 66 deletions(-) create mode 100644 apps/website/public/assets/jobs/codex.png create mode 100644 apps/website/public/assets/jobs/logos.png create mode 100644 apps/website/public/assets/jobs/nimbus.png create mode 100644 apps/website/public/assets/jobs/vac.png create mode 100644 apps/website/public/assets/jobs/waku.png create mode 100644 apps/website/src/pages/jobs/[slug].tsx create mode 100644 apps/website/src/pages/jobs/index.tsx diff --git a/apps/website/public/assets/jobs/codex.png b/apps/website/public/assets/jobs/codex.png new file mode 100644 index 0000000000000000000000000000000000000000..03e6e9f48b25a9cec2fd2dc9bfbce91e816206cf GIT binary patch literal 3388 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 Sw)hkP00000Xf8kK<}r6KsQoJ^%a9x5<5 zml5Ys6s?g&rN8UJ7;7d{gp^EF=7L8JQvU_vORPjIMJ#&WrG<(zS(@1p`JTakyt z#5@Ew!Wy11VFC{sGDOE~*RJIX6)HIQVq#*H!&D%Tf;e_;5C!~HID-Zy5rAZ8bv$(F zP#QRJpk6c7s#Pmz+^$_as#~|N^IY%Vy_GRGY(bFeFAAYRGFgT%EINPwd_87oXVZoa z8>oK$`i?M-8a1L%pFZjM_3PJEv0_CXH*406wr$%+j~+eJ^U0GZQ+#~L{W63S#aGl# z4}}SvtgI~V)vK3ceD2)2ymRMH2TEtpo@EhM$6K~+;qKkLD{L?)IXPLM!F^FATpt0H zf@+)=jsg&7*sx)ol9J+_XJlls*Xz~s-Me=k!F)a+PnzI?4mgdY4 zXYF8C@Yp<9yLN4^SFfJ4AdWY0-sD=fYUz247A;h8?Dn~H=bY!}%$dVGcI;4#8LKpD z(u8l_y5%T|cVTR4jo+-`wNYU7=+S)p_HACca3ROV#pz-zSFYs6ix)eQrB9ze{Pyiz z-nwj(>rtXQ$aiC|s3cI8v2PVs;N14?8Ct2ze@H*emY*Q{B? zr%#_&_>3AgimzV1TF5HEyx;HVnl)=GV|@SqeP<37fAHV|ckI}aXUv$vXU?2a9yDlB zI56^C!^a5j77{#V$`lpk`}glWd-iOWD5Szc;KPRx^ML~gRM90%mhj`pk6A*g?)I25 zWBC31_j*mDmlFugyW6*KZ|fKFR`3DFA3l7@+qZ9LkH_Xixv zh`@m{M)WyyjU&N23BGlHC9?wMs&mGilN!4Xc1$r0d60ganAeI)H%%#Bey`yK(o+(ZxyVCIC!xg_LPMok32y=Gt-c4`byrF{!57OSfd#QT$>grn(Ruw{3 zs#GCya+*1FCP_{S`OX{)5q%Isr5x7!rE}-b+^=6hO<;HK+|fjZq=ee1b?eq4O^!L| z&!1=61U(|ZAnv0uY}>Z2Lb6)5YK1~@F$~$YNw5y1NMu*8T;cWW*E`7u3PKPFP}HLC z=TT3gL&Lf3Hm9bh^2Li6okA}R_CtXb8{tvYEM2-( zlAm;KYDriQ)^yLo{RIY9e+TrSPg%t90znpg5PjpydIg5qbiY z9Jse`-8x>re7TNsf6tyheC^sb7C+K^?%o(jBr+6=$kgGK67e<`kRG?tVqLm)Q6V6- z7dQ@9R2-2xjr$TKZj>fqG1vn(i1e6QziW3zpU;{<*E=g$G5ZTH> z1ucL>KAVTote|d}?CkvRBiT}S(R5R2UZf0Q39rs`5;j@FCim~(-|4*O&6}rHtl4>K z?sX%^jvZ^OfY+@6U=;GVAhWpJ2jmwCH95|)gE-&3d9yb8<~gaEI3XcHL$9eqQ3&3< zcaO`LFYnAlWCFx274SfClZlbcQYi!>2_fs^h+0L;W|A1LwGnC=B)rwDSJS0Smozbf z%=;SZaeiMy0r&ayXR@Gl3i5Tw$_j=7ADG;f&}dxHxWg*& z3zXZK*Ro|xbzoyLbYSSI@jR^Z;>8Qi1-9$DcRv{UY+r&fmz0#`^axPKBqAJvdyM55 zIE54iAc!a>rMC)in)^zXDupAwk3vw`A2QI>LQuDIh(Z`6=^@gP%a5eK(xm3TX1P|* zufoO!QD9g=3G3hcP_rAqz!8O>BtE?-@m}$72>0P5ZnCUO;itXGlDrhA6(~rwqVRS% z6o>t_#uuf9qrpBZA^C`i>3=G`A(-w@edO~}l*mIN8lNch-&9H;y?sQbDtI160WzV; zLj|e4Gz7N}-O(Tq6<{=yFZh0nP;jfSYZl}Y?nwpTu*_d>Jg)os0Y-NFCveONT#JbQ Y1+AZ683=TydH?_b07*qoM6N<$f?g08o&W#< literal 0 HcmV?d00001 diff --git a/apps/website/public/assets/jobs/nimbus.png b/apps/website/public/assets/jobs/nimbus.png new file mode 100644 index 0000000000000000000000000000000000000000..24c9c59e1f026d65024bcef461dbcda78b2621b3 GIT binary patch literal 1234 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 Y1+AZ683=TydH?_b07*qoM6N<$f?g08o&W#< literal 0 HcmV?d00001 diff --git a/apps/website/src/components/feature-list.tsx b/apps/website/src/components/feature-list.tsx index 0b9c2097..2f42d344 100644 --- a/apps/website/src/components/feature-list.tsx +++ b/apps/website/src/components/feature-list.tsx @@ -7,7 +7,7 @@ import type { illustrations } from '@/config/illustrations' type Item = { title: string description: string - image: (typeof illustrations)[keyof typeof illustrations] + icon: (typeof illustrations)[keyof typeof illustrations] } type Props = { @@ -18,15 +18,15 @@ type Props = { export const FeatureList = (props: Props) => { const { list, dark = false } = props return ( -
+
- {list.map(({ title, image, description }, i) => ( + {list.map(({ title, icon, description }, i) => (
- {image.alt} + {icon.alt}
diff --git a/apps/website/src/config/routes.ts b/apps/website/src/config/routes.ts index 8ca80c1d..838d3823 100644 --- a/apps/website/src/config/routes.ts +++ b/apps/website/src/config/routes.ts @@ -38,7 +38,7 @@ export const ROUTES = { { name: 'Blog', href: '/blog' }, { name: 'Translations', href: '/' }, // { name: 'Community groups', href: '/' }, - { name: 'Jobs', href: '/' }, + { name: 'Jobs', href: '/jobs' }, ], Developers: [ { name: 'Repos', href: 'https://github.com/status-im' }, diff --git a/apps/website/src/pages/features/create-community.tsx b/apps/website/src/pages/features/create-community.tsx index c96a73b8..0b449324 100644 --- a/apps/website/src/pages/features/create-community.tsx +++ b/apps/website/src/pages/features/create-community.tsx @@ -35,37 +35,37 @@ const FEATURE_LIST = [ title: 'Decentralized', description: 'Communities are literally powered by their members running the Status Desktop app.', - image: illustrations.doge, + icon: illustrations.doge, }, { title: 'Permissionless', description: 'Nobody can stop you creating a community, because nobody controls Status’ p2p network.', - image: illustrations.mushroom, + icon: illustrations.mushroom, }, { title: 'Self-sovereign', description: 'Each community can set it’s own rules, whatever they are. And is responsible for it’s own actions. ', - image: illustrations.hand, + icon: illustrations.hand, }, { title: '100% Free to use', description: 'No paid tier. No artificially imposed limits. No commission charged on community token sales.', - image: illustrations.duck, + icon: illustrations.duck, }, { title: '100% Open source', description: 'Status itself is a community project. Anyone can build, contribute to and fork it’s source code.', - image: illustrations.flower, + icon: illustrations.flower, }, { title: '100% Freedom', description: 'Status’s mission is to protect free speech, uphold human rights and defend privacy.', - image: illustrations.megaphone, + icon: illustrations.megaphone, }, ] diff --git a/apps/website/src/pages/jobs/[slug].tsx b/apps/website/src/pages/jobs/[slug].tsx new file mode 100644 index 00000000..d8df99b9 --- /dev/null +++ b/apps/website/src/pages/jobs/[slug].tsx @@ -0,0 +1,154 @@ +import { Button, Tag, Text } from '@status-im/components' +import { cx } from 'class-variance-authority' + +import { Breadcrumbs } from '@/components/breadcrumbs' +import { AppLayout, Content } from '@/layouts/app-layout' + +import type { BreadcrumbsProps } from '@/components/breadcrumbs' +import type { GetStaticPaths, GetStaticProps, Page } from 'next' +import type React from 'react' + +type Params = { slug: string } + +const SLUG = 'senior-react-native-ui-developer' + +export const getStaticPaths: GetStaticPaths = async () => { + return { + paths: [ + { + params: { + slug: SLUG, + }, + }, + ], + fallback: false, + } +} + +export const getStaticProps: GetStaticProps = async () => { + // root + const breadcrumbs = [ + { + label: 'Jobs', + // TODO: typesafe + href: '/jobs', + }, + { + label: 'Senior React Native UI Developer', + href: `/jobs/${SLUG}`, + }, + ] + + return { + props: { + breadcrumbs, + }, + } +} + +type Props = { + breadcrumbs: BreadcrumbsProps['items'] +} + +const JobsDetailPage: Page = props => { + const { breadcrumbs } = props + + return ( + + + +
+
+ +

+ Senior React Native UI Developer +

+ + Full time, Remote (Worldwide) + +
+ + {/* CONTENT */} +
+ + The role We’re growing our mobile development team. Join us in + building a fully decentralized, censorship resistant, privacy first + group chat platform that leverages the Ethereum blockchain to enable + individuals and groups worldwide to communicate and transact with + one another freely without restriction. Status is looking for an UI + Engineer to join our mobile development team who will work closely + with Design to transform UI specifications into beautiful, smooth, + performant and near pixel perfect interactive interfaces. The ideal + person will be comfortable working on features end-to-end, has an + eye for design and visual detail, enjoys working with designers as + well as developers, and who can transform reused UI patterns such as + list items into reusable UI components. +
+
+ The ideal candidate will love finessing UI implementations to the + highest levels of quality and will care deeply about things like + code cleanliness, reusability, maintainability, performance, and UI + layout accuracy - as well as doing whatever needed to create a + best-in-class user experience for Status’s users. Willingness to + both learn and share your knowledge with others, + product-orientation, and making sure all team members are aligned + when developing new features, will make you successful in the role. + Status is a fast-paced, flat organization, working on cutting edge + blockchain and decentralized messaging technologies in a dynamic + landscape. We look forward to meeting you! +
+ + + The role We’re growing our mobile development team. Join us in + building a fully decentralized, censorship resistant, privacy first + group chat platform that leverages the Ethereum blockchain to enable + individuals and groups worldwide to communicate and transact with + one another freely without restriction. Status is looking for an UI + Engineer to join our mobile development team who will work closely + with Design to transform UI specifications into beautiful, smooth, + performant and near pixel perfect interactive interfaces. The ideal + person will be comfortable working on features end-to-end, has an + eye for design and visual detail, enjoys working with designers as + well as developers, and who can transform reused UI patterns such as + list items into reusable UI components. +
+
+ The ideal candidate will love finessing UI implementations to the + highest levels of quality and will care deeply about things like + code cleanliness, reusability, maintainability, performance, and UI + layout accuracy - as well as doing whatever needed to create a + best-in-class user experience for Status’s users. Willingness to + both learn and share your knowledge with others, + product-orientation, and making sure all team members are aligned + when developing new features, will make you successful in the role. + Status is a fast-paced, flat organization, working on cutting edge + blockchain and decentralized messaging technologies in a dynamic + landscape. We look forward to meeting you! +
+
+ + {/* FOOTER */} +
+
+ + Apply for this job + + Submit your application here +
+ +
+
+
+ ) +} + +JobsDetailPage.getLayout = function getLayout(page) { + return {page} +} + +export default JobsDetailPage diff --git a/apps/website/src/pages/jobs/index.tsx b/apps/website/src/pages/jobs/index.tsx new file mode 100644 index 00000000..b1418655 --- /dev/null +++ b/apps/website/src/pages/jobs/index.tsx @@ -0,0 +1,276 @@ +import { useRef } from 'react' + +// import codexImage from '@assets/jobs/codex.png' +import logosImage from '@assets/jobs/logos.png' +// import nimbusImage from '@assets/jobs/nimbus.png' +// import vacImage from '@assets/jobs/vac.png' +// import wakuImage from '@assets/jobs/waku.png' +import { Button, Tag, Text } from '@status-im/components' +import { ArrowDownIcon, ArrowRightIcon, ExternalIcon } from '@status-im/icons' +import Image from 'next/image' +import Link from 'next/link' + +import { FeatureList } from '@/components/feature-list' +import { illustrations } from '@/config/illustrations' +import { AppLayout, Content } from '@/layouts/app-layout' + +import type { Page } from 'next' + +// TODO FIX PHOTOS +// TODO FIX FEATURE LIST +// TODO ADD CIRCLES +// TODO CONNECT TO GREENHOUSE API (https://developers.greenhouse.io/job-board.html#retrieve-job-board) + +const OPEN_JOBS = 82 + +const JobsPage: Page = () => { + const openRolesRef = useRef(null) + + const handleScrollToOpenings = () => { + openRolesRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }) + } + + return ( + +
+
+ +
+

+ Join the +
+ decentralized +
+ movement +

+ +
+
+ + We’re hiring! + + {OPEN_JOBS} remote openings +
+ +
+
+ + {/* PHOTOS */} +
+ +
+
+ + +
+ +
+
+ +
+
+ + Status strives to develop open source software that can be used as a + secure communication tool that upholds human rights. We are building + the tools and infrastructure for the advancement of a secure, + private, and open web3. Our core software, Status, is an open + source, Ethereum-based software that gives users the power to chat, + transact, and access a revolutionary world of DApps on the + decentralized web. With the high level goals of preserving the right + to privacy, protecting messages from third parties, and safely + sending, storing and receiving cryptocurrencies with you being the + only one who holds the keys to your funds, Status is providing the + tools and infrastructure to facilitate the creation of communities, + where anyone is welcome to create, join and contribute. +
+
+ 150+ passionate individuals (known as core-contributors) work + together all around the world to facilitate Status’ mission. We care + deeply about our mission, so we have created an environment which + allows core-contributors to complete their work with freedom and + flexibility. We regularly survey the core contributors to gather + insights on their satisfaction with contributing to Status & the + most common answers regarding why they’re satisfied are: our + mission, values, flexibility, friendly environment, non-corporate + culture and our entrepreneurial spirit. +
+
+
+ +
+
+

Perks and benefits

+
+ +
+ +
+
+

Open roles

+
+ +
+ {/* JOB GROUPS */} + {[1, 2, 3].map(i => ( +
+
+

Design

+
+
+ {[ + 'Product designer', + 'Visual designer', + 'Motion designer', + 'Illustrator', + ].map(position => ( + // TODO: discuss hover state + + + {position} + +
+ Full-time +
+ + ))} +
+
+ ))} +
+
+ + {/* TODO FIX TEXTURE */} +
+
+
+

+ Open roles +
+ in our network +

+
+
+ {['Logos', 'Codex', 'Waku', 'Nimbus', 'Vac'].map(name => ( +
+
+ {`${name} + + {name} + +
+ {[1, 2, 3, 4, 5, 6].map(i => ( + + + Protocol Research Engineer + + + + ))} +
+ ))} +
+
+
+
+ ) +} + +const PERKS = [ + { + title: 'Fully remote', + description: 'Work from wherever you want, whenever you want. ', + icon: illustrations.doge, + }, + { + title: 'Unlimited vacation', + description: + 'Take all the time off you need. We need you in your best self.', + icon: illustrations.doge, + }, + { + title: 'Paid team offsites', + description: 'We meet at least once a year somewhere across the world. ', + icon: illustrations.doge, + }, + { + title: 'Hardware stipend', + description: 'Need a laptop to work? We got you covered.', + icon: illustrations.doge, + }, + { + title: 'Co-working stipend', + description: + 'We offer a $250 stipend for you to work from a co-working space.', + icon: illustrations.doge, + }, + { + title: 'Get paid in crypto', + description: 'We offer to pay salaries in SNT, ETH, USDC or fiat.', + icon: illustrations.doge, + }, + { + title: 'SNT Bonus', + description: 'We reward hard work and longevity with a bonus in SNT. ', + icon: illustrations.doge, + }, + { + title: 'Referral fee', + description: + 'We offer a $5000 SNT referral fee so bring your friends aboard.', + icon: illustrations.doge, + }, + { + title: 'Headspace subscription', + description: + 'Take care of your mind with a premium headspace account, for free.', + icon: illustrations.doge, + }, +] + +JobsPage.getLayout = function getLayout(page) { + return {page} +} + +export default JobsPage diff --git a/apps/website/src/pages/keycard.tsx b/apps/website/src/pages/keycard.tsx index 2ae86ba8..f1965c7c 100644 --- a/apps/website/src/pages/keycard.tsx +++ b/apps/website/src/pages/keycard.tsx @@ -132,36 +132,36 @@ const FEATURE_LIST = [ title: 'Top tier security', description: 'Our hardware security successfully passed Common Criteria EAL6+ certification ', - image: illustrations.doge, + icon: illustrations.doge, }, { title: 'Keys stored in the card', description: 'It’s impossible for Status or any government to extract your private keys from keycard', - image: illustrations.mushroom, + icon: illustrations.mushroom, }, { title: 'Feature 3', description: 'Here will say something more about security and how kc is revolutionary when it comes to security', - image: illustrations.hand, + icon: illustrations.hand, }, { title: 'Mobile friendly', description: 'Keycard is using NFC which is natively embedded in all mobile phones', - image: illustrations.duck, + icon: illustrations.duck, }, { title: 'No need to charge', description: 'Keycard has no battery so it’s always ready to go.', - image: illustrations.flower, + icon: illustrations.flower, }, { title: 'Easy to carry around', description: 'Its credit card form factor is small and convenient to fit in any wallet.', - image: illustrations.megaphone, + icon: illustrations.megaphone, }, ] diff --git a/apps/website/src/styles/global.css b/apps/website/src/styles/global.css index 96817d4e..1ef4927b 100644 --- a/apps/website/src/styles/global.css +++ b/apps/website/src/styles/global.css @@ -6,6 +6,9 @@ .container { @apply mx-auto max-w-[1504px] px-5 lg:px-40; } + .border-dashed-default { + @apply border-dashed border-neutral-80/20; + } } @keyframes gradient { diff --git a/packages/components/src/button/button.tsx b/packages/components/src/button/button.tsx index 2b766d62..77f49172 100644 --- a/packages/components/src/button/button.tsx +++ b/packages/components/src/button/button.tsx @@ -1,14 +1,13 @@ import { cloneElement, forwardRef } from 'react' import { styled } from '@tamagui/core' -import { Pressable } from 'react-native' +import { View } from 'react-native' import { Text } from '../text' import type { TextProps } from '../text' import type { GetVariants, MapVariant, PressableProps } from '../types' import type { Ref } from 'react' -import type { View } from 'react-native' type Variants = GetVariants @@ -94,10 +93,9 @@ const _Button = forwardRef(Button) export { _Button as Button } export type { Props as ButtonProps } -const Base = styled(Pressable, { - tag: 'button', +const Base = styled(View, { name: 'Button', - accessibilityRole: 'button', + role: 'button', display: 'flex', flexDirection: 'row', diff --git a/packages/components/src/dynamic-button/dynamic-button.tsx b/packages/components/src/dynamic-button/dynamic-button.tsx index 8c11c098..b495eff1 100644 --- a/packages/components/src/dynamic-button/dynamic-button.tsx +++ b/packages/components/src/dynamic-button/dynamic-button.tsx @@ -2,7 +2,7 @@ import { forwardRef } from 'react' import { ArrowDownIcon, MentionIcon } from '@status-im/icons' import { styled } from '@tamagui/core' -import { Pressable } from 'react-native' +import { View } from 'react-native' import { Shadow } from '../shadow' import { Text } from '../text' @@ -10,7 +10,6 @@ import { Text } from '../text' import type { GetVariants, PressableProps } from '../types' import type { ColorTokens } from '@tamagui/core' import type { Ref } from 'react' -import type { View } from 'react-native' type Variants = GetVariants @@ -50,10 +49,9 @@ const _DynamicButton = forwardRef(DynamicButton) export { _DynamicButton as DynamicButton } export type { Props as DynamicButtonProps } -const Button = styled(Pressable, { +const Button = styled(View, { name: 'DynamicButton', - tag: 'button', - accessibilityRole: 'button', + role: 'button', cursor: 'pointer', userSelect: 'none', diff --git a/packages/components/src/hooks/use-pressable-colors.tsx b/packages/components/src/hooks/use-pressable-colors.tsx index 8d4177f8..f9792a3e 100644 --- a/packages/components/src/hooks/use-pressable-colors.tsx +++ b/packages/components/src/hooks/use-pressable-colors.tsx @@ -1,7 +1,6 @@ import { useState } from 'react' import type { PressableProps } from '../types' -import type { MouseEvent } from 'react-native' import type { ColorTokens } from 'tamagui' type Config = { @@ -49,20 +48,20 @@ export const usePressableColors = ( return { color: styles[key], pressableProps: { - onHoverIn: event => { - props.onHoverIn?.(event as unknown as MouseEvent) + onHoverIn: (...args) => { + props.onHoverIn?.(...args) setHovered(true) }, - onHoverOut: event => { - props.onHoverOut?.(event as unknown as MouseEvent) + onHoverOut: (...args) => { + props.onHoverOut?.(...args) setHovered(false) }, - onPressIn: event => { - props.onPressIn?.(event) + onPressIn: (...args) => { + props.onPressIn?.(...args) setPressed(true) }, - onPressOut: event => { - props.onPressOut?.(event) + onPressOut: (...args) => { + props.onPressOut?.(...args) setPressed(false) }, } as const, diff --git a/packages/components/src/icon-button/icon-button.tsx b/packages/components/src/icon-button/icon-button.tsx index 81679000..7cb4f4c0 100644 --- a/packages/components/src/icon-button/icon-button.tsx +++ b/packages/components/src/icon-button/icon-button.tsx @@ -1,13 +1,12 @@ import { cloneElement, forwardRef } from 'react' -import { Pressable } from 'react-native' +import { View } from 'react-native' import { styled } from 'tamagui' import { usePressableColors } from '../hooks/use-pressable-colors' import type { GetVariants, PressableProps } from '../types' import type { Ref } from 'react' -import type { View } from 'react-native' type Variants = GetVariants @@ -63,10 +62,9 @@ const _IconButton = forwardRef(IconButton) export { _IconButton as IconButton } export type { Props as IconButtonProps } -const Base = styled(Pressable, { +const Base = styled(View, { name: 'IconButton', - tag: 'button', - accessibilityRole: 'button', + role: 'button', cursor: 'pointer', userSelect: 'none', diff --git a/packages/components/src/input/input.tsx b/packages/components/src/input/input.tsx index 0c9ea4d2..0b7a0712 100644 --- a/packages/components/src/input/input.tsx +++ b/packages/components/src/input/input.tsx @@ -106,7 +106,6 @@ const _Input = (props: Props, ref: Ref) => { {Boolean(onClear) && !!value && ( @@ -117,8 +116,9 @@ Tabs.Content = Content export { Tabs } export type { Props as TabsProps } -const TriggerBase = styled(Pressable, { - tag: 'button', +const TriggerBase = styled(View, { + name: 'Trigger', + role: 'button', flexDirection: 'row', alignItems: 'center', diff --git a/packages/components/src/tag/tag.tsx b/packages/components/src/tag/tag.tsx index bc9d4b09..e79bee56 100644 --- a/packages/components/src/tag/tag.tsx +++ b/packages/components/src/tag/tag.tsx @@ -1,6 +1,7 @@ import { createElement } from 'react' -import { Stack, styled } from '@tamagui/core' +import { styled } from '@tamagui/core' +import { View } from 'react-native' import { Text } from '../text' import { getCustomStyles } from './get-custom-styles' @@ -9,6 +10,7 @@ import type { TextProps } from '../text' import type { IconProps } from '@status-im/icons' import type { ColorTokens } from '@tamagui/core' import type { ComponentType } from 'react' +import type { PressableProps } from 'react-native' type Props = { size: 32 | 24 @@ -16,7 +18,7 @@ type Props = { label?: string selected?: boolean disabled?: boolean - onPress?: () => void + onPress?: PressableProps['onPress'] color?: ColorTokens | `#${string}` } @@ -54,8 +56,11 @@ const Tag = (props: Props) => { selected={selected} disabled={disabled} iconOnly={Boolean(icon && !label)} - onPress={() => onPress?.()} {...getCustomStyles(props)} + {...(onPress && { + role: 'button', + onPress, + })} > {renderIcon()} {label && ( @@ -70,11 +75,10 @@ const Tag = (props: Props) => { export { Tag } export type { Props as TagProps } -const Base = styled(Stack, { - tag: 'tag', +const Base = styled(View, { name: 'Tag', - accessibilityRole: 'button', + userSelect: 'none', display: 'flex', flexDirection: 'row', alignItems: 'center', @@ -85,7 +89,6 @@ const Base = styled(Stack, { backgroundColor: '$white-100', animation: 'fast', - cursor: 'pointer', hoverStyle: { borderColor: '$neutral-30', diff --git a/packages/components/src/types.ts b/packages/components/src/types.ts index 070b496b..66da6448 100644 --- a/packages/components/src/types.ts +++ b/packages/components/src/types.ts @@ -8,12 +8,12 @@ import type { import type { PressableProps as NativePressableProps } from 'react-native' type PressableProps = { - onHoverIn?: Exclude - onHoverOut?: NativePressableProps['onHoverOut'] - onPress?: NativePressableProps['onPress'] - onPressIn?: NativePressableProps['onPressIn'] - onPressOut?: NativePressableProps['onPressOut'] - onLongPress?: NativePressableProps['onLongPress'] + onHoverIn?: VoidFunction + onHoverOut?: VoidFunction + onPress?: VoidFunction + onPressIn?: VoidFunction + onPressOut?: VoidFunction + onLongPress?: VoidFunction delayHoverIn?: NativePressableProps['delayHoverIn'] delayHoverOut?: NativePressableProps['delayHoverOut'] delayLongPress?: NativePressableProps['delayLongPress']