diff --git a/.eslintignore b/.eslintignore index 9f2c1facb..199129c4b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,7 +1,6 @@ *.css .changeset dist -scripts/* *.config.js .DS_Store node_modules diff --git a/jest.config.js b/jest.config.js index 4d704b7ed..4d8d94366 100644 --- a/jest.config.js +++ b/jest.config.js @@ -18,7 +18,7 @@ module.exports = { ] }, transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx)$'], - setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect', './scripts/setup-test.ts'], + setupFilesAfterEnv: ['./scripts/setup-test.ts'], testTimeout: 10000, globals: { 'ts-jest': { diff --git a/package.json b/package.json index ef0be3493..fb1769f0b 100644 --- a/package.json +++ b/package.json @@ -43,11 +43,10 @@ "@storybook/react-vite": "^7.4.0", "@swc/core": "^1.3.84", "@swc/jest": "^0.2.29", - "@testing-library/dom": "^8.1.0", - "@testing-library/jest-dom": "^5.16.4", + "@testing-library/dom": "^9.3.3", + "@testing-library/jest-dom": "^6.1.3", "@testing-library/react": "^14.0.0", - "@testing-library/react-hooks": "^8.0.1", - "@testing-library/user-event": "^14.4.3", + "@testing-library/user-event": "^14.5.1", "@types/jest": "^29.5.4", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", diff --git a/packages/components/src/Input/BaseInput.tsx b/packages/components/src/Input/BaseInput.tsx index efc41f3b6..dc674fd0b 100644 --- a/packages/components/src/Input/BaseInput.tsx +++ b/packages/components/src/Input/BaseInput.tsx @@ -1,5 +1,5 @@ import { ValidationState } from '@react-types/shared'; -import { forwardRef, InputHTMLAttributes, ReactNode, useEffect, useRef, useState } from 'react'; +import { FocusEvent, forwardRef, InputHTMLAttributes, ReactNode, useEffect, useRef, useState } from 'react'; import { Sizes, Spacing } from '@interlay/theme'; import { Field, FieldProps, useFieldProps } from '../Field'; @@ -22,6 +22,8 @@ type Props = { padding?: { top?: Spacing; bottom?: Spacing; left?: Spacing; right?: Spacing }; validationState?: ValidationState; onChange?: (e: React.ChangeEvent) => void; + onFocus?: (e: FocusEvent) => void; + onBlur?: (e: FocusEvent) => void; }; type NativeAttrs = Omit, keyof Props>; @@ -29,7 +31,7 @@ type NativeAttrs = Omit, keyof Props>; type InheritAttrs = Omit< HelperTextProps & Pick, - keyof Props & NativeAttrs + keyof (Props & NativeAttrs) >; type BaseInputProps = Props & NativeAttrs & InheritAttrs; diff --git a/packages/components/src/Input/Input.style.tsx b/packages/components/src/Input/Input.style.tsx index 63a618d28..2f9b558ac 100644 --- a/packages/components/src/Input/Input.style.tsx +++ b/packages/components/src/Input/Input.style.tsx @@ -32,6 +32,7 @@ const StyledBaseInput = styled.input` $adornments.bottom ? theme.input.overflow.large.text : theme.input[$size].text}; line-height: ${theme.lineHeight.base}; font-weight: ${({ $size }) => theme.input[$size].weight}; + text-overflow: ellipsis; background-color: ${({ $isDisabled }) => ($isDisabled ? theme.input.disabled.bg : theme.input.background)}; overflow: hidden; diff --git a/packages/components/src/List/List.tsx b/packages/components/src/List/List.tsx index 062f6dc45..e7882e9b8 100644 --- a/packages/components/src/List/List.tsx +++ b/packages/components/src/List/List.tsx @@ -25,8 +25,11 @@ type NativeAttrs = Omit; type ListProps = Props & NativeAttrs & InheritAttrs; const List = forwardRef( - ({ variant = 'primary', direction = 'column', onSelectionChange, ...props }, ref): JSX.Element => { - const ariaProps = { onSelectionChange, ...props }; + ( + { variant = 'primary', direction = 'column', onSelectionChange, selectionMode, selectedKeys, ...props }, + ref + ): JSX.Element => { + const ariaProps = { onSelectionChange, selectionMode, selectedKeys, ...props }; const state = useListState(ariaProps); const listRef = useDOMRef(ref); const { gridProps } = useGridList(ariaProps, state, listRef); diff --git a/packages/components/src/NumberInput/NumberInput.tsx b/packages/components/src/NumberInput/NumberInput.tsx index 9b1948519..d68149fa4 100644 --- a/packages/components/src/NumberInput/NumberInput.tsx +++ b/packages/components/src/NumberInput/NumberInput.tsx @@ -25,7 +25,7 @@ type InheritAttrs = Omit< keyof Props | 'errorMessageProps' | 'descriptionProps' | 'disabled' | 'required' | 'readOnly' >; -type AriaAttrs = Omit, (keyof Props & InheritAttrs) | 'onChange'>; +type AriaAttrs = Omit, keyof (Props & InheritAttrs)>; type NumberInputProps = Props & InheritAttrs & AriaAttrs; diff --git a/packages/components/src/Overlay/OpenTransition.tsx b/packages/components/src/Overlay/OpenTransition.tsx index f77564c1d..969e98df2 100644 --- a/packages/components/src/Overlay/OpenTransition.tsx +++ b/packages/components/src/Overlay/OpenTransition.tsx @@ -20,9 +20,9 @@ type OpenTransitionProps = Props & InheritAttrs; const OpenTransition = (props: OpenTransitionProps): any => { // Do not apply any transition if in chromatic (based on react-spectrum) - // if (process.env.CHROMATIC) { - // return Children.map(props.children, (child) => child && cloneElement(child as any, { isOpen: props.in })); - // } + if (process.env.NODE_ENV === 'test') { + return Children.map(props.children, (child) => child && cloneElement(child as any, { isOpen: props.in })); + } return ( diff --git a/packages/components/src/Select/Select.stories.tsx b/packages/components/src/Select/Select.stories.tsx index 5a8c2a9e6..e48754156 100644 --- a/packages/components/src/Select/Select.stories.tsx +++ b/packages/components/src/Select/Select.stories.tsx @@ -35,8 +35,7 @@ export default { layout: 'centered' }, args: { - label: 'Coin', - withModal: false + label: 'Coin' }, render: Render } as Meta; diff --git a/packages/components/src/Select/Select.tsx b/packages/components/src/Select/Select.tsx index 37ab84cb0..2123c1ce2 100644 --- a/packages/components/src/Select/Select.tsx +++ b/packages/components/src/Select/Select.tsx @@ -129,7 +129,7 @@ const Select = @@ -153,7 +153,7 @@ const Select = void; onChangeTicker?: (ticker?: string) => void; + onValueChange?: (value?: string | number) => void; selectProps?: Omit; }; @@ -43,17 +43,22 @@ const TokenInput = forwardRef( hidden, className, onClickBalance, + onValueChange, onChangeTicker, selectProps, placeholder = '0', errorMessage, description, + value: valueProp, + defaultValue, + onBlur, ...props }, ref ): JSX.Element => { const inputRef = useDOMRef(ref); + const [value, setValue] = useState(defaultValue); const [ticker, setTicker] = useState( (selectProps?.defaultValue as string) || (typeof tickerProp === 'string' ? tickerProp : tickerProp?.text) || '' ); @@ -61,6 +66,7 @@ const TokenInput = forwardRef( const { labelProps, fieldProps } = useLabel({ label, ...props }); const selectHelperTextId = useId(); + const inputId = useId(); const itemsArr = Array.from(selectProps?.items || []); const isSelectAdornment = itemsArr.length > 1; @@ -72,11 +78,19 @@ const TokenInput = forwardRef( setTicker(selectProps.value as string); }, [selectProps?.value]); + useEffect(() => { + if (valueProp === undefined) return; + + setValue(valueProp); + }, [valueProp]); + const handleClickBalance = () => { if (!balance) return; - triggerChangeEvent(inputRef, balance); + inputRef.current?.focus(); onClickBalance?.(balance); + setValue(balance); + onValueChange?.(balance); }; const handleTokenChange = (ticker: Key) => { @@ -84,6 +98,20 @@ const TokenInput = forwardRef( setTicker(ticker as string); }; + const handleChange = (e: ChangeEvent) => onValueChange?.(e.target.value); + + const handleBlur = (e: FocusEvent) => { + const relatedTargetEl = e.relatedTarget as HTMLButtonElement; + + if (!relatedTargetEl) return; + + const isTargetingMaxBtn = relatedTargetEl.getAttribute('aria-controls') === inputId; + + if (!isTargetingMaxBtn) { + onBlur?.(e); + } + }; + // Prioritise Number Input description and error message const hasSelectHelperText = !errorMessage && !description && (selectProps?.errorMessage || selectProps?.description); @@ -105,6 +133,15 @@ const TokenInput = forwardRef( ) : null; const hasLabel = !!label || balance !== undefined; + const hasLabelWithBalance = hasLabel && balance !== undefined; + + const numberInputProps: Partial = hasLabelWithBalance + ? { + id: inputId, + onBlur: handleBlur, + onChange: handleChange + } + : { onBlur, onChange: handleChange }; return (