diff --git a/packages/components/package.json b/packages/components/package.json index 7ead589e5..bbedfe3b9 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -79,7 +79,8 @@ "@react-stately/toggle": "^3.6.1", "@react-stately/tooltip": "^3.4.3", "@react-stately/tree": "^3.7.1", - "react-transition-group": "^4.4.5" + "react-transition-group": "^4.4.5", + "usehooks-ts": "^2.9.2" }, "peerDependencies": { "react": ">=18", diff --git a/packages/components/src/Accordion/AccordionItemRegion.tsx b/packages/components/src/Accordion/AccordionItemRegion.tsx index 082b93af8..cc7277d3c 100644 --- a/packages/components/src/Accordion/AccordionItemRegion.tsx +++ b/packages/components/src/Accordion/AccordionItemRegion.tsx @@ -1,4 +1,5 @@ -import { HTMLAttributes, useEffect, useRef, useState } from 'react'; +import { HTMLAttributes } from 'react'; +import { useElementSize } from 'usehooks-ts'; import { StyledAccordionItemContent, StyledAccordionItemRegion } from './Accordion.style'; @@ -11,13 +12,7 @@ type NativeAttrs = Omit, keyof Props>; type AccordionItemRegionProps = Props & NativeAttrs; const AccordionItemRegion = ({ isExpanded, children, ...props }: AccordionItemRegionProps): JSX.Element => { - const ref = useRef(null); - const [height, setHeight] = useState(0); - - // Updates height in case anything changed in children - useEffect(() => { - setHeight(ref.current?.clientHeight || 0); - }, [isExpanded]); + const [ref, { height }] = useElementSize(); return ( diff --git a/packages/components/src/Radio/Radio.stories.tsx b/packages/components/src/Radio/Radio.stories.tsx index e74d96c20..2ceed6b3b 100644 --- a/packages/components/src/Radio/Radio.stories.tsx +++ b/packages/components/src/Radio/Radio.stories.tsx @@ -5,8 +5,12 @@ import { Radio, RadioGroupProps, RadioGroup } from '.'; const Render = (args: RadioGroupProps) => ( - BTC - ETH + + BTC + + + ETH + ); diff --git a/packages/components/src/Radio/Radio.style.tsx b/packages/components/src/Radio/Radio.style.tsx index efc7d349a..8a1ab16f2 100644 --- a/packages/components/src/Radio/Radio.style.tsx +++ b/packages/components/src/Radio/Radio.style.tsx @@ -12,6 +12,7 @@ type StyledRadioGroupProps = { type StyledLabelProps = { $isDisabled?: boolean; + $flex?: number | string | boolean; }; type StyledButtonProps = { @@ -20,6 +21,8 @@ type StyledButtonProps = { }; const StyledRadioGroup = styled(Flex)` + width: 100%; + label { margin-right: ${({ $orientation, $gap }) => $orientation === 'horizontal' && $gap && theme.spacing[$gap]}; margin-bottom: ${({ $orientation, $gap }) => $orientation === 'vertical' && $gap && theme.spacing[$gap]}; @@ -32,6 +35,7 @@ const StyledLabel = styled(Label)` gap: ${theme.spacing.spacing2}; align-items: center; opacity: ${({ $isDisabled }) => $isDisabled && 0.5}; + flex: ${({ $flex }) => (typeof $flex === 'boolean' ? '1' : $flex)}; `; const StyledInput = styled.input` diff --git a/packages/components/src/Radio/Radio.tsx b/packages/components/src/Radio/Radio.tsx index 879f104ff..e8ebb6696 100644 --- a/packages/components/src/Radio/Radio.tsx +++ b/packages/components/src/Radio/Radio.tsx @@ -6,12 +6,13 @@ import { HTMLAttributes, forwardRef, useRef } from 'react'; import { Span, TextProps } from '../Text'; -import { useRadioProvider } from './RadioContext'; import { StyledButton, StyledInput, StyledLabel } from './Radio.style'; +import { useRadioProvider } from './RadioContext'; type Props = { labelProps?: TextProps; labelPlacement?: Extract; + flex?: string | number | boolean; }; type NativeAttrs = Omit, keyof Props>; @@ -22,7 +23,7 @@ type RadioProps = Props & NativeAttrs & InheritAttrs; // TODO: determine if isInvalid is necessary const Radio = forwardRef( - ({ labelProps, isDisabled: isDisabledProp, children, className, style, ...props }, ref): JSX.Element => { + ({ labelProps, isDisabled: isDisabledProp, children, className, style, flex, ...props }, ref): JSX.Element => { let { hoverProps, isHovered } = useHover({ isDisabled: isDisabledProp }); const labelRef = useDOMRef(ref); @@ -45,6 +46,7 @@ const Radio = forwardRef( {...labelProps} {...hoverProps} ref={labelRef} + $flex={flex} $isDisabled={isDisabled} className={className} style={style} diff --git a/packages/components/src/TextLink/TextLink.stories.tsx b/packages/components/src/TextLink/TextLink.stories.tsx index 9fe88881a..f40c55f85 100644 --- a/packages/components/src/TextLink/TextLink.stories.tsx +++ b/packages/components/src/TextLink/TextLink.stories.tsx @@ -22,3 +22,9 @@ export const External: StoryObj = { icon: true } }; + +export const WithoutHref: StoryObj = { + args: { + href: undefined + } +}; diff --git a/packages/components/src/TextLink/TextLink.style.tsx b/packages/components/src/TextLink/TextLink.style.tsx index 6a252392d..601e16553 100644 --- a/packages/components/src/TextLink/TextLink.style.tsx +++ b/packages/components/src/TextLink/TextLink.style.tsx @@ -5,7 +5,7 @@ import { Colors, FontSize, FontWeight } from '@interlay/theme'; type BaseTextLinkProps = { $color?: Colors; - $underlined?: boolean; + $isQuiet?: boolean; $size?: FontSize; $weight?: FontWeight; }; @@ -17,11 +17,11 @@ const BaseTextLink = styled.a` font-size: ${({ $size }) => $size && theme.text[$size]}; line-height: ${({ $size }) => resolveHeight($size)}; font-weight: ${({ $weight }) => $weight && theme.fontWeight[$weight]}; - text-decoration: ${(props) => props.$underlined && 'underline'}; + text-decoration: ${(props) => (props.$isQuiet ? 'none' : 'underline')}; &:hover, &:focus-visible { - text-decoration: ${(props) => (props.$underlined ? 'underline double' : 'underline')}; + text-decoration: underline; } `; diff --git a/packages/components/src/TextLink/TextLink.tsx b/packages/components/src/TextLink/TextLink.tsx index 1e37c9edc..754caeeed 100644 --- a/packages/components/src/TextLink/TextLink.tsx +++ b/packages/components/src/TextLink/TextLink.tsx @@ -9,7 +9,7 @@ import { BaseTextLink, StyledIcon } from './TextLink.style'; type Props = { color?: Colors; external?: boolean; - underlined?: boolean; + isQuiet?: boolean; size?: FontSize; weight?: FontWeight; icon?: boolean; @@ -23,12 +23,21 @@ type TextLinkProps = Props & NativeAttrs & AriaAttrs; // TODO: merge this with CTALink const TextLink = forwardRef( - ({ color = 'primary', external, underlined, size, weight, icon, children, ...props }, ref): JSX.Element => { + ( + { color = 'primary', external, isQuiet, size, weight, icon, children, href, className, ...props }, + ref + ): JSX.Element => { const linkRef = useDOMRef(ref); + const elementType = href ? 'a' : 'span'; + + const externalProps = external ? { target: '_blank', rel: 'noreferrer' } : undefined; + const ariaProps = { ...props, - ...(external && { target: '_blank', rel: 'noreferrer' }) + ...externalProps, + href, + elementType }; const { linkProps } = useLink(ariaProps, linkRef); @@ -37,12 +46,12 @@ const TextLink = forwardRef( {children} {icon && } diff --git a/packages/components/src/TextLink/__tests__/TextLink.test.tsx b/packages/components/src/TextLink/__tests__/TextLink.test.tsx index ec50483cc..72376db11 100644 --- a/packages/components/src/TextLink/__tests__/TextLink.test.tsx +++ b/packages/components/src/TextLink/__tests__/TextLink.test.tsx @@ -1,4 +1,4 @@ -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { createRef } from 'react'; import { testA11y } from '@interlay/test-utils'; @@ -22,4 +22,12 @@ describe('TextLink', () => { it('should pass a11y', async () => { await testA11y(link); }); + + it.only('should render pressable link as a span', async () => { + const handlePress = jest.fn(); + + render(link); + + expect(screen.getByRole('link').tagName).toBe('SPAN'); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0b10f175..06f0f1fd8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -164,7 +164,7 @@ importers: version: 7.3.0(@swc/core@1.3.99)(ts-node@10.9.1)(typescript@5.3.2) turbo: specifier: latest - version: 1.10.16 + version: 1.11.3 typescript: specifier: '>=3.0.0' version: 5.3.2 @@ -297,6 +297,9 @@ importers: react-transition-group: specifier: ^4.4.5 version: 4.4.5(react-dom@18.2.0)(react@18.2.0) + usehooks-ts: + specifier: ^2.9.2 + version: 2.9.2(react-dom@18.2.0)(react@18.2.0) devDependencies: '@interlay/test-utils': specifier: workspace:* @@ -13320,64 +13323,64 @@ packages: yargs: 17.7.2 dev: true - /turbo-darwin-64@1.10.16: - resolution: {integrity: sha512-+Jk91FNcp9e9NCLYlvDDlp2HwEDp14F9N42IoW3dmHI5ZkGSXzalbhVcrx3DOox3QfiNUHxzWg4d7CnVNCuuMg==} + /turbo-darwin-64@1.11.3: + resolution: {integrity: sha512-IsOOg2bVbIt3o/X8Ew9fbQp5t1hTHN3fGNQYrPQwMR2W1kIAC6RfbVD4A9OeibPGyEPUpwOH79hZ9ydFH5kifw==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-darwin-arm64@1.10.16: - resolution: {integrity: sha512-jqGpFZipIivkRp/i+jnL8npX0VssE6IAVNKtu573LXtssZdV/S+fRGYA16tI46xJGxSAivrZ/IcgZrV6Jk80bw==} + /turbo-darwin-arm64@1.11.3: + resolution: {integrity: sha512-FsJL7k0SaPbJzI/KCnrf/fi3PgCDCjTliMc/kEFkuWVA6Httc3Q4lxyLIIinz69q6JTx8wzh6yznUMzJRI3+dg==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-linux-64@1.10.16: - resolution: {integrity: sha512-PpqEZHwLoizQ6sTUvmImcRmACyRk9EWLXGlqceogPZsJ1jTRK3sfcF9fC2W56zkSIzuLEP07k5kl+ZxJd8JMcg==} + /turbo-linux-64@1.11.3: + resolution: {integrity: sha512-SvW7pvTVRGsqtSkII5w+wriZXvxqkluw5FO/MNAdFw0qmoov+PZ237+37/NgArqE3zVn1GX9P6nUx9VO+xcQAg==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-linux-arm64@1.10.16: - resolution: {integrity: sha512-TMjFYz8to1QE0fKVXCIvG/4giyfnmqcQIwjdNfJvKjBxn22PpbjeuFuQ5kNXshUTRaTJihFbuuCcb5OYFNx4uw==} + /turbo-linux-arm64@1.11.3: + resolution: {integrity: sha512-YhUfBi1deB3m+3M55X458J6B7RsIS7UtM3P1z13cUIhF+pOt65BgnaSnkHLwETidmhRh8Dl3GelaQGrB3RdCDw==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-windows-64@1.10.16: - resolution: {integrity: sha512-+jsf68krs0N66FfC4/zZvioUap/Tq3sPFumnMV+EBo8jFdqs4yehd6+MxIwYTjSQLIcpH8KoNMB0gQYhJRLZzw==} + /turbo-windows-64@1.11.3: + resolution: {integrity: sha512-s+vEnuM2TiZuAUUUpmBHDr6vnNbJgj+5JYfnYmVklYs16kXh+EppafYQOAkcRIMAh7GjV3pLq5/uGqc7seZeHA==} cpu: [x64] os: [win32] requiresBuild: true dev: true optional: true - /turbo-windows-arm64@1.10.16: - resolution: {integrity: sha512-sKm3hcMM1bl0B3PLG4ifidicOGfoJmOEacM5JtgBkYM48ncMHjkHfFY7HrJHZHUnXM4l05RQTpLFoOl/uIo2HQ==} + /turbo-windows-arm64@1.11.3: + resolution: {integrity: sha512-ZR5z5Zpc7cASwfdRAV5yNScCZBsgGSbcwiA/u3farCacbPiXsfoWUkz28iyrx21/TRW0bi6dbsB2v17swa8bjw==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /turbo@1.10.16: - resolution: {integrity: sha512-2CEaK4FIuSZiP83iFa9GqMTQhroW2QryckVqUydmg4tx78baftTOS0O+oDAhvo9r9Nit4xUEtC1RAHoqs6ZEtg==} + /turbo@1.11.3: + resolution: {integrity: sha512-RCJOUFcFMQNIGKSjC9YmA5yVP1qtDiBA0Lv9VIgrXraI5Da1liVvl3VJPsoDNIR9eFMyA/aagx1iyj6UWem5hA==} hasBin: true optionalDependencies: - turbo-darwin-64: 1.10.16 - turbo-darwin-arm64: 1.10.16 - turbo-linux-64: 1.10.16 - turbo-linux-arm64: 1.10.16 - turbo-windows-64: 1.10.16 - turbo-windows-arm64: 1.10.16 + turbo-darwin-64: 1.11.3 + turbo-darwin-arm64: 1.11.3 + turbo-linux-64: 1.11.3 + turbo-linux-arm64: 1.11.3 + turbo-windows-64: 1.11.3 + turbo-windows-arm64: 1.11.3 dev: true /tween-functions@1.2.0: @@ -13667,6 +13670,18 @@ packages: tslib: 2.6.2 dev: true + /usehooks-ts@2.9.2(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-fOzPeG01rs51CGYzqgioP/zs9v1Cgpe+zcXeqJPlDHYfdfG/wjsdjBWHJi+Ph1JgQAGUrDo5sJbPlaZd+Z9lxw==} + engines: {node: '>=16.15.0'} + requiresBuild: true + peerDependencies: + react: ^18.2.0 + react-dom: ^18.2.0 + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true