@@ -44,6 +44,7 @@
|
||||
"@web3modal/react": "^2.0.0-rc.2",
|
||||
"ace-builds": "^1.14.0",
|
||||
"bignumber.js": "^9.1.0",
|
||||
"chakra-react-select": "^4.4.3",
|
||||
"d3": "^7.6.1",
|
||||
"dayjs": "^1.11.5",
|
||||
"dom-to-image": "^2.6.0",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"foo": "bar"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"foo": "bar",
|
||||
"baz": ["baz","baz","baz"]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"id": 1,
|
||||
"title": "iPhone 9",
|
||||
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce nec leo odio. Vivamus iaculis faucibus tempus. Duis dapibus, ligula eu consequat ornare, sapien nisl laoreet arcu, eu pharetra dolor urna eget ligula. Sed facilisis pretium risus in finibus. Maecenas egestas orci euismod venenatis aliquam. Vivamus sed pretium dui, vitae porta ipsum. Donec elit tortor, imperdiet eu nulla at, condimentum consequat nisi. Phasellus mollis sem aliquam dolor lacinia bibendum. Mauris pulvinar vestibulum sodales. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nullam vitae facilisis odio. Mauris in ultricies nulla. Sed ornare consequat nulla. Cras pellentesque, erat at gravida rutrum, erat neque facilisis magna, et iaculis enim tortor ornare felis. Quisque mollis dignissim magna. In vitae ultricies felis. Quisque in ipsum non lorem ornare elementum vel vitae nisl. Integer luctus pulvinar dui a eleifend. Aliquam iaculis odio vitae metus ullamcorper, sed semper turpis consectetur. In convallis, justo a aliquet rhoncus, erat metus fringilla turpis, eget imperdiet purus est quis magna. Curabitur ornare mattis malesuada. In vitae lacinia est. Sed sit amet magna eget dolor tempus euismod.",
|
||||
"price": 549,
|
||||
"discountPercentage": 12.96,
|
||||
"rating": 4.69,
|
||||
"stock": 94,
|
||||
"brand": "Apple",
|
||||
"category": "smartphones",
|
||||
"thumbnail": "https://i.dummyjson.com/data/products/1/thumbnail.jpg",
|
||||
"images": [
|
||||
"https://i.dummyjson.com/data/products/1/1.jpg",
|
||||
"https://i.dummyjson.com/data/products/1/2.jpg",
|
||||
"https://i.dummyjson.com/data/products/1/3.jpg",
|
||||
"https://i.dummyjson.com/data/products/1/4.jpg",
|
||||
"https://i.dummyjson.com/data/products/1/thumbnail.jpg"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
const sizes = {
|
||||
sm: {
|
||||
field: {
|
||||
px: '0',
|
||||
height: '36px',
|
||||
},
|
||||
},
|
||||
md: {
|
||||
field: {
|
||||
px: '0',
|
||||
height: '56px',
|
||||
},
|
||||
},
|
||||
lg: {
|
||||
field: {
|
||||
px: '0',
|
||||
height: '76px',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const FancySelect = {
|
||||
sizes,
|
||||
};
|
||||
|
||||
export default FancySelect;
|
||||
@@ -4,6 +4,7 @@ import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
|
||||
import { getColor } from '@chakra-ui/theme-tools';
|
||||
|
||||
import getDefaultFormColors from '../utils/getDefaultFormColors';
|
||||
import FancySelect from './FancySelect';
|
||||
import FormLabel from './FormLabel';
|
||||
import Input from './Input';
|
||||
import Textarea from './Textarea';
|
||||
@@ -57,22 +58,32 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction
|
||||
label: activeLabelStyles,
|
||||
'input, textarea': activeInputStyles,
|
||||
},
|
||||
'&[data-active=true] label': activeLabelStyles,
|
||||
|
||||
// label styles
|
||||
label: FormLabel.sizes?.[size](props) || {},
|
||||
'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': activeLabelStyles,
|
||||
'input[aria-invalid=true] + label, textarea[aria-invalid=true] + label': {
|
||||
[`
|
||||
input[aria-invalid=true] + label,
|
||||
textarea[aria-invalid=true] + label,
|
||||
&[aria-invalid=true] label
|
||||
`]: {
|
||||
color: getColor(theme, errorColor),
|
||||
},
|
||||
|
||||
// input styles
|
||||
input: Input.sizes?.[size].field,
|
||||
'input[aria-autocomplete=list]': FancySelect.sizes[size].field,
|
||||
textarea: Textarea.sizes?.[size],
|
||||
'input, textarea': {
|
||||
padding: inputPx,
|
||||
},
|
||||
'input:not(:placeholder-shown), textarea:not(:placeholder-shown)': activeInputStyles,
|
||||
'input[disabled] + label, textarea[disabled] + label': {
|
||||
[`
|
||||
input[disabled] + label,
|
||||
textarea[disabled] + label,
|
||||
&[aria-disabled=true] label
|
||||
`]: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
|
||||
@@ -80,7 +91,11 @@ function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunction
|
||||
'input:not(:placeholder-shown) + label .chakra-form__required-indicator, textarea:not(:placeholder-shown) + label .chakra-form__required-indicator': {
|
||||
color: getColor(theme, focusPlaceholderColor),
|
||||
},
|
||||
'input[aria-invalid=true] + label .chakra-form__required-indicator, textarea[aria-invalid=true] + label .chakra-form__required-indicator': {
|
||||
[`
|
||||
input[aria-invalid=true] + label .chakra-form__required-indicator,
|
||||
textarea[aria-invalid=true] + label .chakra-form__required-indicator,
|
||||
&[aria-invalid=true] .chakra-form__required-indicator
|
||||
`]: {
|
||||
color: getColor(theme, errorColor),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -63,6 +63,9 @@ const sizes = {
|
||||
_focusWithin: {
|
||||
padding: '16px 24px 2px 24px',
|
||||
},
|
||||
'&[data-fancy=true]': {
|
||||
right: '36px',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -78,6 +81,9 @@ const sizes = {
|
||||
_focusWithin: {
|
||||
padding: '10px 16px 2px 16px',
|
||||
},
|
||||
'&[data-fancy=true]': {
|
||||
right: '36px',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { test, expect } from '@playwright/experimental-ct-react';
|
||||
import React from 'react';
|
||||
|
||||
import TestApp from 'playwright/TestApp';
|
||||
|
||||
import ContractVerificationForm from './ContractVerificationForm';
|
||||
|
||||
const hooksConfig = {
|
||||
router: {
|
||||
query: {},
|
||||
},
|
||||
};
|
||||
|
||||
test('flatten source code method +@dark-mode +@mobile', async({ mount, page }) => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<ContractVerificationForm/>
|
||||
</TestApp>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
|
||||
await page.getByText(/flattened source code/i).click();
|
||||
await page.getByText(/optimization enabled/i).click();
|
||||
await page.getByText(/add contract libraries/i).click();
|
||||
await page.locator('button[aria-label="add"]').click();
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('standard input json method', async({ mount, page }) => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<ContractVerificationForm/>
|
||||
</TestApp>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
|
||||
await page.getByText(/via standard/i).click();
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('sourcify method +@dark-mode +@mobile', async({ mount, page }) => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<ContractVerificationForm/>
|
||||
</TestApp>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
|
||||
await page.getByText(/via sourcify/i).click();
|
||||
await page.getByText(/upload files/i).click();
|
||||
await page.locator('input[name="sources"]').setInputFiles([
|
||||
'./playwright/mocks/file_mock_1.json',
|
||||
'./playwright/mocks/file_mock_2.json',
|
||||
'./playwright/mocks/file_mock_with_very_long_name.json',
|
||||
]);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('multi-part files method', async({ mount, page }) => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<ContractVerificationForm/>
|
||||
</TestApp>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
|
||||
await page.getByText(/via multi-part files/i).click();
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('vyper contract method', async({ mount, page }) => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<ContractVerificationForm/>
|
||||
</TestApp>,
|
||||
{ hooksConfig },
|
||||
);
|
||||
|
||||
await page.getByText(/vyper contract/i).click();
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 45 KiB |
@@ -26,7 +26,6 @@ const ContractVerificationFieldCode = ({ isVyper }: Props) => {
|
||||
isInvalid={ Boolean(error) }
|
||||
isDisabled={ formState.isSubmitting }
|
||||
required
|
||||
maxLength={ 255 }
|
||||
/>
|
||||
<InputPlaceholder text="Contract code" error={ error }/>
|
||||
</FormControl>
|
||||
@@ -40,6 +39,7 @@ const ContractVerificationFieldCode = ({ isVyper }: Props) => {
|
||||
control={ control }
|
||||
render={ renderControl }
|
||||
rules={{ required: true }}
|
||||
defaultValue=""
|
||||
/>
|
||||
{ isVyper ? null : (
|
||||
<>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { Code, Select, Checkbox } from '@chakra-ui/react';
|
||||
import { Code, Checkbox } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import type { ControllerRenderProps } from 'react-hook-form';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
|
||||
import type { FormFields } from '../types';
|
||||
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import FancySelect from 'ui/shared/FancySelect/FancySelect';
|
||||
|
||||
import ContractVerificationFormRow from '../ContractVerificationFormRow';
|
||||
|
||||
const COMPILERS = [
|
||||
@@ -25,27 +28,38 @@ interface Props {
|
||||
|
||||
const ContractVerificationFieldCompiler = ({ isVyper }: Props) => {
|
||||
const [ isNightly, setIsNightly ] = React.useState(false);
|
||||
const { formState, control } = useFormContext<FormFields>();
|
||||
const { formState, control, getValues, resetField } = useFormContext<FormFields>();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const options = React.useMemo(() => (
|
||||
[
|
||||
...COMPILERS, ...(isNightly ? COMPILERS_NIGHTLY : []),
|
||||
].map((option) => ({ label: option, value: option }))
|
||||
), [ isNightly ]);
|
||||
|
||||
const handleCheckboxChange = React.useCallback(() => {
|
||||
if (isNightly) {
|
||||
const field = getValues('compiler');
|
||||
field.value.includes('nightly') && resetField('compiler', { defaultValue: null });
|
||||
}
|
||||
setIsNightly(prev => !prev);
|
||||
}, []);
|
||||
}, [ getValues, isNightly, resetField ]);
|
||||
|
||||
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'compiler'>}) => {
|
||||
const error = 'compiler' in formState.errors ? formState.errors.compiler : undefined;
|
||||
|
||||
return (
|
||||
<Select
|
||||
<FancySelect
|
||||
{ ...field }
|
||||
size={{ base: 'md', lg: 'lg' }}
|
||||
options={ options }
|
||||
size={ isMobile ? 'md' : 'lg' }
|
||||
placeholder="Compiler"
|
||||
isInvalid={ Boolean(error) }
|
||||
isDisabled={ formState.isSubmitting }
|
||||
>
|
||||
{ [ ...COMPILERS, ...(isNightly ? COMPILERS_NIGHTLY : []) ].map((option) => <option key={ option } value={ option }>{ option }</option>) }
|
||||
</Select>
|
||||
error={ error }
|
||||
isRequired
|
||||
/>
|
||||
);
|
||||
}, [ formState.errors, formState.isSubmitting, isNightly ]);
|
||||
}, [ formState.errors, formState.isSubmitting, isMobile, options ]);
|
||||
|
||||
return (
|
||||
<ContractVerificationFormRow>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { Link, Select } from '@chakra-ui/react';
|
||||
import { Link } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import type { ControllerRenderProps } from 'react-hook-form';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
|
||||
import type { FormFields } from '../types';
|
||||
|
||||
import useIsMobile from 'lib/hooks/useIsMobile';
|
||||
import FancySelect from 'ui/shared/FancySelect/FancySelect';
|
||||
|
||||
import ContractVerificationFormRow from '../ContractVerificationFormRow';
|
||||
|
||||
const VERSIONS = [
|
||||
@@ -15,22 +18,27 @@ const VERSIONS = [
|
||||
|
||||
const ContractVerificationFieldEvmVersion = () => {
|
||||
const { formState, control } = useFormContext<FormFields>();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const options = React.useMemo(() => (
|
||||
VERSIONS.map((option) => ({ label: option, value: option }))
|
||||
), [ ]);
|
||||
|
||||
const renderControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'evm_version'>}) => {
|
||||
const error = 'evm_version' in formState.errors ? formState.errors.evm_version : undefined;
|
||||
|
||||
return (
|
||||
<Select
|
||||
<FancySelect
|
||||
{ ...field }
|
||||
size={{ base: 'md', lg: 'lg' }}
|
||||
options={ options }
|
||||
size={ isMobile ? 'md' : 'lg' }
|
||||
placeholder="EVM Version"
|
||||
isInvalid={ Boolean(error) }
|
||||
isDisabled={ formState.isSubmitting }
|
||||
>
|
||||
{ VERSIONS.map((option) => <option key={ option } value={ option }>{ option }</option>) }
|
||||
</Select>
|
||||
error={ error }
|
||||
isRequired
|
||||
/>
|
||||
);
|
||||
}, [ formState.errors, formState.isSubmitting ]);
|
||||
}, [ formState.errors, formState.isSubmitting, isMobile, options ]);
|
||||
|
||||
return (
|
||||
<ContractVerificationFormRow>
|
||||
|
||||
@@ -39,6 +39,7 @@ const ContractVerificationFieldLibraries = () => {
|
||||
size="lg"
|
||||
onChange={ handleCheckboxChange }
|
||||
mt={ 9 }
|
||||
isDisabled={ formState.isSubmitting }
|
||||
>
|
||||
Add contract libraries
|
||||
</Checkbox>
|
||||
@@ -52,6 +53,7 @@ const ContractVerificationFieldLibraries = () => {
|
||||
onAddFieldClick={ handleAddFieldClick }
|
||||
onRemoveFieldClick={ handleRemoveFieldClick }
|
||||
error={ 'libraries' in formState.errors ? formState.errors.libraries?.[index] : undefined }
|
||||
isDisabled={ formState.isSubmitting }
|
||||
/>
|
||||
)) }
|
||||
</>
|
||||
|
||||
@@ -24,9 +24,10 @@ interface Props {
|
||||
};
|
||||
onAddFieldClick: (index: number) => void;
|
||||
onRemoveFieldClick: (index: number) => void;
|
||||
isDisabled?: boolean;
|
||||
}
|
||||
|
||||
const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, onAddFieldClick, onRemoveFieldClick, error }: Props) => {
|
||||
const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, onAddFieldClick, onRemoveFieldClick, error, isDisabled }: Props) => {
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
const renderNameControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, `libraries.${ number }.name`>}) => {
|
||||
@@ -36,13 +37,14 @@ const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, on
|
||||
{ ...field }
|
||||
required
|
||||
isInvalid={ Boolean(error?.name) }
|
||||
isDisabled={ isDisabled }
|
||||
maxLength={ 255 }
|
||||
autoComplete="off"
|
||||
/>
|
||||
<InputPlaceholder text="Library name (.sol file)" error={ error?.name }/>
|
||||
</FormControl>
|
||||
);
|
||||
}, [ error?.name ]);
|
||||
}, [ error?.name, isDisabled ]);
|
||||
|
||||
const renderAddressControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, `libraries.${ number }.address`>}) => {
|
||||
return (
|
||||
@@ -50,13 +52,14 @@ const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, on
|
||||
<Input
|
||||
{ ...field }
|
||||
isInvalid={ Boolean(error?.address) }
|
||||
isDisabled={ isDisabled }
|
||||
required
|
||||
autoComplete="off"
|
||||
/>
|
||||
<InputPlaceholder text="Library address (0x...)" error={ error?.address }/>
|
||||
</FormControl>
|
||||
);
|
||||
}, [ error?.address ]);
|
||||
}, [ error?.address, isDisabled ]);
|
||||
|
||||
const handleAddButtonClick = React.useCallback(() => {
|
||||
onAddFieldClick(index);
|
||||
@@ -84,6 +87,7 @@ const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, on
|
||||
h="30px"
|
||||
onClick={ handleRemoveButtonClick }
|
||||
icon={ <Icon as={ minusIcon } w="20px" h="20px"/> }
|
||||
isDisabled={ isDisabled }
|
||||
/>
|
||||
) }
|
||||
{ fieldsLength < LIMIT && (
|
||||
@@ -94,6 +98,7 @@ const ContractVerificationFieldLibraryItem = ({ control, index, fieldsLength, on
|
||||
h="30px"
|
||||
onClick={ handleAddButtonClick }
|
||||
icon={ <Icon as={ plusIcon } w="20px" h="20px"/> }
|
||||
isDisabled={ isDisabled }
|
||||
/>
|
||||
) }
|
||||
</Flex>
|
||||
|
||||
@@ -41,6 +41,7 @@ const ContractVerificationFieldName = ({ hint }: Props) => {
|
||||
control={ control }
|
||||
render={ renderControl }
|
||||
rules={{ required: true }}
|
||||
defaultValue=""
|
||||
/>
|
||||
{ hint ? <span>{ hint }</span> : (
|
||||
<>
|
||||
|
||||
@@ -29,13 +29,13 @@ const ContractVerificationFieldOptimization = () => {
|
||||
|
||||
const renderInputControl = React.useCallback(({ field }: {field: ControllerRenderProps<FormFields, 'optimization_runs'>}) => {
|
||||
return (
|
||||
<FormControl variant="floating" id={ field.name } size={{ base: 'md', lg: 'lg' }}>
|
||||
<FormControl variant="floating" id={ field.name } size={{ base: 'md', lg: 'lg' }} isRequired>
|
||||
<Input
|
||||
{ ...field }
|
||||
required
|
||||
maxLength={ 255 }
|
||||
isDisabled={ formState.isSubmitting }
|
||||
autoComplete="off"
|
||||
type="number"
|
||||
/>
|
||||
<InputPlaceholder text="Optimization runs"/>
|
||||
</FormControl>
|
||||
@@ -58,6 +58,7 @@ const ContractVerificationFieldOptimization = () => {
|
||||
control={ control }
|
||||
render={ renderInputControl }
|
||||
rules={{ required: true }}
|
||||
defaultValue=""
|
||||
/>
|
||||
</ContractVerificationFormRow>
|
||||
) }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { Option } from 'ui/shared/FancySelect/types';
|
||||
|
||||
export type VerificationMethod = 'flatten_source_code' | 'standard_input' | 'sourcify' | 'multi_part_file' | 'vyper_contract'
|
||||
|
||||
export interface ContractLibrary {
|
||||
@@ -8,8 +10,8 @@ export interface FormFieldsFlattenSourceCode {
|
||||
method: 'flatten_source_code';
|
||||
is_yul: boolean;
|
||||
name: string;
|
||||
compiler: string;
|
||||
evm_version: string;
|
||||
compiler: Option;
|
||||
evm_version: Option;
|
||||
is_optimization_enabled: boolean;
|
||||
optimization_runs: string;
|
||||
code: string;
|
||||
@@ -20,7 +22,7 @@ export interface FormFieldsFlattenSourceCode {
|
||||
export interface FormFieldsStandardInput {
|
||||
method: 'standard_input';
|
||||
name: string;
|
||||
compiler: string;
|
||||
compiler: Option;
|
||||
sources: Array<File>;
|
||||
}
|
||||
|
||||
@@ -31,8 +33,8 @@ export interface FormFieldsSourcify {
|
||||
|
||||
export interface FormFieldsMultiPartFile {
|
||||
method: 'multi_part_file';
|
||||
compiler: string;
|
||||
evm_version: string;
|
||||
compiler: Option;
|
||||
evm_version: Option;
|
||||
is_optimization_enabled: boolean;
|
||||
optimization_runs: string;
|
||||
sources: Array<File>;
|
||||
@@ -41,7 +43,7 @@ export interface FormFieldsMultiPartFile {
|
||||
export interface FormFieldsVyperContract {
|
||||
method: 'vyper_contract';
|
||||
name: string;
|
||||
compiler: string;
|
||||
compiler: Option;
|
||||
code: string;
|
||||
abi_encoded_args: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import { test, expect } from '@playwright/experimental-ct-react';
|
||||
import _noop from 'lodash/noop';
|
||||
import React from 'react';
|
||||
|
||||
import TestApp from 'playwright/TestApp';
|
||||
|
||||
import FancySelect from './FancySelect';
|
||||
|
||||
const OPTIONS = [
|
||||
{ value: 'v0.8.17+commit.8df45f5f', label: 'v0.8.17+commit.8df45f5f' },
|
||||
{ value: 'v0.8.16+commit.07a7930e', label: 'v0.8.16+commit.07a7930e' },
|
||||
{ value: 'v0.8.15+commit.e14f2714', label: 'v0.8.15+commit.e14f2714' },
|
||||
];
|
||||
|
||||
test.use({ viewport: { width: 500, height: 300 } });
|
||||
|
||||
const defaultProps = {
|
||||
options: OPTIONS,
|
||||
isRequired: true,
|
||||
placeholder: 'Compiler',
|
||||
name: 'compiler',
|
||||
onChange: _noop,
|
||||
};
|
||||
|
||||
[ 'md' as const, 'lg' as const ].forEach((size) => {
|
||||
test.describe(`size ${ size } +@dark-mode`, () => {
|
||||
test('empty', async({ mount, page }) => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<FancySelect
|
||||
{ ...defaultProps }
|
||||
size={ size }
|
||||
value={ null }
|
||||
/>
|
||||
</TestApp>,
|
||||
);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
|
||||
await component.getByLabel(/compiler/i).focus();
|
||||
await component.getByLabel(/compiler/i).type('1');
|
||||
await expect(page).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('filled', async({ mount }) => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<FancySelect
|
||||
{ ...defaultProps }
|
||||
size={ size }
|
||||
value={ OPTIONS[0] }
|
||||
/>
|
||||
</TestApp>,
|
||||
);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('error', async({ mount }) => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<FancySelect
|
||||
{ ...defaultProps }
|
||||
size={ size }
|
||||
value={ null }
|
||||
error={{
|
||||
type: 'unknown',
|
||||
message: 'cannot be empty',
|
||||
}}
|
||||
/>
|
||||
</TestApp>,
|
||||
);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
|
||||
await component.getByLabel(/compiler/i).focus();
|
||||
await component.getByLabel(/compiler/i).type('1');
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
|
||||
test('disabled', async({ mount }) => {
|
||||
const component = await mount(
|
||||
<TestApp>
|
||||
<FancySelect
|
||||
{ ...defaultProps }
|
||||
size={ size }
|
||||
value={ OPTIONS[0] }
|
||||
isDisabled
|
||||
/>
|
||||
</TestApp>,
|
||||
);
|
||||
|
||||
await expect(component).toHaveScreenshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
import { FormControl, useToken, useColorMode } from '@chakra-ui/react';
|
||||
import type { Size, CSSObjectWithLabel, OptionsOrGroups, GroupBase, SingleValue, MultiValue } from 'chakra-react-select';
|
||||
import { Select } from 'chakra-react-select';
|
||||
import React from 'react';
|
||||
import type { FieldError, FieldErrorsImpl, Merge } from 'react-hook-form';
|
||||
|
||||
import type { Option } from './types';
|
||||
|
||||
import { getChakraStyles } from 'ui/shared/FancySelect/utils';
|
||||
import InputPlaceholder from 'ui/shared/InputPlaceholder';
|
||||
|
||||
interface Props {
|
||||
size?: Size; // we don't have styles for sm select/input with floating label yet
|
||||
options: OptionsOrGroups<Option, GroupBase<Option>>;
|
||||
placeholder: string;
|
||||
name: string;
|
||||
onChange: (newValue: SingleValue<Option> | MultiValue<Option>) => void;
|
||||
onBlur?: () => void;
|
||||
isDisabled?: boolean;
|
||||
isRequired?: boolean;
|
||||
error?: Merge<FieldError, FieldErrorsImpl<Option>> | undefined;
|
||||
value?: SingleValue<Option> | MultiValue<Option>;
|
||||
}
|
||||
|
||||
const FancySelect = ({ size = 'md', options, placeholder, name, onChange, onBlur, isDisabled, isRequired, error, value }: Props) => {
|
||||
const menuZIndex = useToken('zIndices', 'dropdown');
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
const styles = React.useMemo(() => ({
|
||||
menuPortal: (provided: CSSObjectWithLabel) => ({ ...provided, zIndex: menuZIndex }),
|
||||
}), [ menuZIndex ]);
|
||||
|
||||
const chakraStyles = React.useMemo(() => getChakraStyles(colorMode), [ colorMode ]);
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
variant="floating"
|
||||
size={ size }
|
||||
isRequired={ isRequired }
|
||||
{ ...(error ? { 'aria-invalid': true } : {}) }
|
||||
{ ...(isDisabled ? { 'aria-disabled': true } : {}) }
|
||||
{ ...(value ? { 'data-active': true } : {}) }
|
||||
>
|
||||
<Select
|
||||
menuPortalTarget={ window.document.body }
|
||||
placeholder=""
|
||||
name={ name }
|
||||
options={ options }
|
||||
size={ size }
|
||||
styles={ styles }
|
||||
chakraStyles={ chakraStyles }
|
||||
useBasicStyles
|
||||
onChange={ onChange }
|
||||
onBlur={ onBlur }
|
||||
isDisabled={ isDisabled }
|
||||
isRequired={ isRequired }
|
||||
isInvalid={ Boolean(error) }
|
||||
value={ value }
|
||||
/>
|
||||
<InputPlaceholder
|
||||
text={ placeholder }
|
||||
error={ error }
|
||||
isFancy
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(React.forwardRef(FancySelect));
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,4 @@
|
||||
export interface Option {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import type { ColorMode } from '@chakra-ui/react';
|
||||
import type { Size, ChakraStylesConfig } from 'chakra-react-select';
|
||||
|
||||
import type { Option } from './types';
|
||||
|
||||
function getValueContainerStyles(size?: Size) {
|
||||
switch (size) {
|
||||
case 'sm':
|
||||
case 'md': {
|
||||
return {
|
||||
paddingLeft: 4,
|
||||
};
|
||||
}
|
||||
case 'lg': {
|
||||
return {
|
||||
paddingLeft: 6,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSingleValueStyles(size?: Size) {
|
||||
switch (size) {
|
||||
case 'sm':
|
||||
case 'md': {
|
||||
return {
|
||||
top: '26px',
|
||||
};
|
||||
}
|
||||
case 'lg': {
|
||||
return {
|
||||
top: '38px',
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getChakraStyles: (colorMode: ColorMode) => ChakraStylesConfig<Option> = (colorMode) => {
|
||||
const emptyInputBorderColor = colorMode === 'dark' ? 'gray.700' : 'gray.100';
|
||||
const filledInputBorderColor = colorMode === 'dark' ? 'gray.600' : 'gray.300';
|
||||
|
||||
return {
|
||||
control: (provided, state) => ({
|
||||
...provided,
|
||||
borderColor: state.hasValue ? filledInputBorderColor : emptyInputBorderColor,
|
||||
}),
|
||||
inputContainer: (provided) => ({
|
||||
...provided,
|
||||
py: 0,
|
||||
mx: 0,
|
||||
}),
|
||||
valueContainer: (provided, state) => ({
|
||||
...provided,
|
||||
...getValueContainerStyles(state.selectProps.size),
|
||||
py: 0,
|
||||
}),
|
||||
singleValue: (provided, state) => ({
|
||||
...provided,
|
||||
mx: 0,
|
||||
transform: 'none',
|
||||
...getSingleValueStyles(state.selectProps.size),
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
export { getChakraStyles };
|
||||
@@ -5,9 +5,11 @@ import type { FieldError } from 'react-hook-form';
|
||||
interface Props {
|
||||
text: string;
|
||||
error?: Partial<FieldError>;
|
||||
className?: string;
|
||||
isFancy?: boolean;
|
||||
}
|
||||
|
||||
const InputPlaceholder = ({ text, error }: Props) => {
|
||||
const InputPlaceholder = ({ text, error, className, isFancy }: Props) => {
|
||||
let errorMessage = error?.message;
|
||||
|
||||
if (!errorMessage && error?.type === 'pattern') {
|
||||
@@ -15,11 +17,14 @@ const InputPlaceholder = ({ text, error }: Props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<FormLabel>
|
||||
<FormLabel
|
||||
className={ className }
|
||||
{ ...(isFancy ? { 'data-fancy': true } : {}) }
|
||||
>
|
||||
<chakra.span>{ text }</chakra.span>
|
||||
{ errorMessage && <chakra.span order={ 3 } whiteSpace="pre"> - { errorMessage }</chakra.span> }
|
||||
</FormLabel>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputPlaceholder;
|
||||
export default chakra(InputPlaceholder);
|
||||
|
||||
@@ -40,6 +40,7 @@ const FileInput = <Values extends FieldValues, Names extends Path<Values>>({ chi
|
||||
ref={ ref }
|
||||
accept={ accept }
|
||||
multiple={ multiple }
|
||||
name={ field.name }
|
||||
/>
|
||||
{ children }
|
||||
</InputGroup>
|
||||
|
||||
@@ -1017,6 +1017,13 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.12.0", "@babel/runtime@^7.8.7":
|
||||
version "7.20.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b"
|
||||
integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.11"
|
||||
|
||||
"@babel/runtime@^7.17.2", "@babel/runtime@^7.5.5":
|
||||
version "7.20.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd"
|
||||
@@ -1894,7 +1901,7 @@
|
||||
source-map "^0.5.7"
|
||||
stylis "4.1.3"
|
||||
|
||||
"@emotion/cache@^11.10.5":
|
||||
"@emotion/cache@^11.10.5", "@emotion/cache@^11.4.0":
|
||||
version "11.10.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.5.tgz#c142da9351f94e47527ed458f7bbbbe40bb13c12"
|
||||
integrity sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==
|
||||
@@ -1934,7 +1941,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f"
|
||||
integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==
|
||||
|
||||
"@emotion/react@^11.10.4":
|
||||
"@emotion/react@^11.10.4", "@emotion/react@^11.8.1":
|
||||
version "11.10.5"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.5.tgz#95fff612a5de1efa9c0d535384d3cfa115fe175d"
|
||||
integrity sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==
|
||||
@@ -2363,6 +2370,18 @@
|
||||
"@ethersproject/properties" "^5.7.0"
|
||||
"@ethersproject/strings" "^5.7.0"
|
||||
|
||||
"@floating-ui/core@^1.0.5":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.1.0.tgz#0a1dee4bbce87ff71602625d33f711cafd8afc08"
|
||||
integrity sha512-zbsLwtnHo84w1Kc8rScAo5GMk1GdecSlrflIbfnEBJwvTSj1SL6kkOYV+nHraMCPEy+RNZZUaZyL8JosDGCtGQ==
|
||||
|
||||
"@floating-ui/dom@^1.0.1":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.1.0.tgz#29fea1344fdef15b6ba270a733d20b7134fee5c2"
|
||||
integrity sha512-TSogMPVxbRe77QCj1dt8NmRiJasPvuc+eT5jnJ6YpLqgOD2zXc5UA3S1qwybN+GVCDNdKfpKy1oj8RpzLJvh6A==
|
||||
dependencies:
|
||||
"@floating-ui/core" "^1.0.5"
|
||||
|
||||
"@humanwhocodes/config-array@^0.11.8":
|
||||
version "0.11.8"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
|
||||
@@ -4030,6 +4049,13 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-transition-group@^4.4.0":
|
||||
version "4.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
|
||||
integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*":
|
||||
version "18.0.21"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.21.tgz#b8209e9626bb00a34c76f55482697edd2b43cc67"
|
||||
@@ -5352,6 +5378,13 @@ caniuse-lite@^1.0.30001332, caniuse-lite@^1.0.30001400:
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz#5f459215192a024c99e3e3a53aac310fc7cf24e6"
|
||||
integrity sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==
|
||||
|
||||
chakra-react-select@^4.4.3:
|
||||
version "4.4.3"
|
||||
resolved "https://registry.yarnpkg.com/chakra-react-select/-/chakra-react-select-4.4.3.tgz#678fcb25b90b9f977628694d1a9d49d072e01128"
|
||||
integrity sha512-anDgJyYUpIapTmUbgXB+Iw5hJ90hOPvgoUPUaYdO5q9zY2VBFhQ1L0gBMqWAQxiKUmuHpwQypf8sPoVtd0b3KA==
|
||||
dependencies:
|
||||
react-select "5.7.0"
|
||||
|
||||
[email protected]:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
|
||||
@@ -6175,6 +6208,14 @@ dom-accessibility-api@^0.5.9:
|
||||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56"
|
||||
integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==
|
||||
|
||||
dom-helpers@^5.0.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
|
||||
integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.8.7"
|
||||
csstype "^3.0.2"
|
||||
|
||||
dom-serializer@^1.0.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
|
||||
@@ -8830,6 +8871,11 @@ [email protected]:
|
||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
||||
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
|
||||
|
||||
memoize-one@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
|
||||
integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
@@ -9584,7 +9630,7 @@ prompts@^2.0.1:
|
||||
kleur "^3.0.3"
|
||||
sisteransi "^1.0.5"
|
||||
|
||||
prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||
prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||
@@ -9804,6 +9850,21 @@ react-scroll@^1.8.7:
|
||||
lodash.throttle "^4.1.1"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
[email protected]:
|
||||
version "5.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.7.0.tgz#82921b38f1fcf1471a0b62304da01f2896cd8ce6"
|
||||
integrity sha512-lJGiMxCa3cqnUr2Jjtg9YHsaytiZqeNOKeibv6WF5zbK/fPegZ1hg3y/9P1RZVLhqBTs0PfqQLKuAACednYGhQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.0"
|
||||
"@emotion/cache" "^11.4.0"
|
||||
"@emotion/react" "^11.8.1"
|
||||
"@floating-ui/dom" "^1.0.1"
|
||||
"@types/react-transition-group" "^4.4.0"
|
||||
memoize-one "^6.0.0"
|
||||
prop-types "^15.6.0"
|
||||
react-transition-group "^4.3.0"
|
||||
use-isomorphic-layout-effect "^1.1.2"
|
||||
|
||||
react-style-singleton@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4"
|
||||
@@ -9813,6 +9874,16 @@ react-style-singleton@^2.2.1:
|
||||
invariant "^2.2.4"
|
||||
tslib "^2.0.0"
|
||||
|
||||
react-transition-group@^4.3.0:
|
||||
version "4.4.5"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
|
||||
integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
dom-helpers "^5.0.1"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
[email protected]:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
|
||||
@@ -10965,6 +11036,11 @@ use-font-face-observer@^1.2.1:
|
||||
fontfaceobserver "2.1.0"
|
||||
react "17.0.2"
|
||||
|
||||
use-isomorphic-layout-effect@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb"
|
||||
integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==
|
||||
|
||||
use-sidecar@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2"
|
||||
|
||||