Skip to content

Commit

Permalink
Fix processing of bool types, cleanup strings
Browse files Browse the repository at this point in the history
  • Loading branch information
cgero-eth committed Nov 29, 2024
1 parent 602237f commit bff61b3
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 44 deletions.
22 changes: 8 additions & 14 deletions src/modules/assets/copy/modulesCopy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,14 @@ export const modulesCopy = {
RAW: 'Raw',
},
},
proposalActionsItemDecodedView: {
valueHelper: 'Amount of ETH to transfer in the transaction',
valueLabel: 'Value',
},
proposalActionsItemFormField: {
required: (label: string) => `${label} is required.`,
boolean: (label: string) => `${label} must be set to "true" or "false".`,
address: (label: string) => `${label} is not a valid address.`,
},
proposalActionsItemRawView: {
to: 'To',
data: 'Data',
value: 'Value',
copyButton: 'Copy data',
proposalActionsDecoder: {
copyData: 'Copy data',
add: 'Add',
validation: {
required: (label: string) => `${label} is required.`,
boolean: (label: string) => `${label} must be set to "true" or "false".`,
address: (label: string) => `${label} is not a valid address.`,
},
},
proposalActionChangeMembers: {
summary: 'Summary',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import type { Meta, StoryObj } from '@storybook/react';
import { FormProvider, useForm } from 'react-hook-form';
import { generateProposalAction } from '../proposalActionsTestUtils';
import { ProposalActionsDecoder } from './proposalActionsDecoder';
import { ProposalActionsDecoderView, type IProposalActionsDecoderProps } from './proposalActionsDecoder.api';
import {
ProposalActionsDecoderMode,
ProposalActionsDecoderView,
type IProposalActionsDecoderProps,
} from './proposalActionsDecoder.api';

const defaultRender = (props: IProposalActionsDecoderProps) => {
const methods = useForm({ mode: 'onTouched', defaultValues: props.action });
Expand Down Expand Up @@ -74,6 +78,7 @@ export const Default: Story = {
export const ReadOnly: Story = {
args: {
view: ProposalActionsDecoderView.DECODED,
mode: ProposalActionsDecoderMode.READ,
action: generateProposalAction({
inputData: {
function: 'approve',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,20 @@ export const ProposalActionsDecoder: React.FC<IProposalActionsDecoderProps> = (p
const { value, data, inputData } = action;

const { copy } = useGukModulesContext();

const { watch, setValue } = useFormContext<IProposalAction>(mode === ProposalActionsDecoderMode.EDIT);

const dataFieldName = proposalActionsDecoderUtils.getFieldName('data', formPrefix) as 'data';

const updateEncodedData = useCallback(
(formValues: DeepPartial<IProposalAction>) => {
const functionParameters = formValues.inputData?.parameters?.map((parameter) => parameter?.value);
const actionAbi = [{ type: 'function', name: inputData?.function, inputs: inputData?.parameters }];
const functionParameters = proposalActionsDecoderUtils.formValuesToFunctionParameters(formValues);
const actionAbi = [{ type: 'function', inputs: inputData?.parameters }];
let data = '0x';

try {
data = encodeFunctionData({ abi: actionAbi, args: functionParameters });
} catch (error: unknown) {
// Form values are not valid, ignore error.
} finally {
// @ts-expect-error Limitation of react-hook-form, ignore error
setValue(dataFieldName, data);
Expand Down Expand Up @@ -69,25 +70,21 @@ export const ProposalActionsDecoder: React.FC<IProposalActionsDecoderProps> = (p
fieldName="value"
mode={mode}
formPrefix={formPrefix}
parameter={{
name: 'value',
notice: copy.proposalActionsItemDecodedView.valueHelper,
value: value,
type: 'uint',
}}
parameter={{ name: 'value', value: value, type: 'uint' }}
/>
)}
<ProposalActionsDecoderTextField
fieldName="data"
mode={mode}
formPrefix={formPrefix}
parameter={{ name: 'data', value: data, type: 'bytes' }}
// Render the data field as hidden on decoded view to register the field on the form on EDIT mode
className={view === ProposalActionsDecoderView.DECODED ? 'hidden' : undefined}
component="textarea"
/>
{view === ProposalActionsDecoderView.RAW && mode === ProposalActionsDecoderMode.READ && (
<Button variant="tertiary" size="md" onClick={handleCopyDataClick} className="self-end">
{copy.proposalActionsItemRawView.copyButton}
{copy.proposalActionsDecoder.copyData}
</Button>
)}
{view === ProposalActionsDecoderView.DECODED &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import classNames from 'classnames';
import { useId, useState } from 'react';
import { Button, IconType, InputContainer } from '../../../../../../core';
import { useFormContext } from '../../../../../hooks';
import { useGukModulesContext } from '../../../../gukModulesProvider';
import type { IProposalActionInputDataParameter } from '../../proposalActionsDefinitions';
import { type IProposalActionsDecoderProps, ProposalActionsDecoderMode } from '../proposalActionsDecoder.api';
import { ProposalActionsDecoderTextField } from '../proposalActionsDecoderTextField';
Expand Down Expand Up @@ -31,6 +32,7 @@ export const ProposalActionsDecoderField: React.FC<IProposalActionsDecoderField>
const { notice, type, name } = parameter;

const inputId = useId();
const { copy } = useGukModulesContext();
const { setValue, getValues, unregister } = useFormContext(mode === ProposalActionsDecoderMode.EDIT);

const isArray = proposalActionsDecoderUtils.isArrayType(type);
Expand Down Expand Up @@ -73,10 +75,13 @@ export const ProposalActionsDecoderField: React.FC<IProposalActionsDecoderField>
setNestedParameters(newNestedParameters);
};

const inputLabels = !hideLabels ? { label: name, helpText: notice } : undefined;

return (
<InputContainer id={inputId} useCustomWrapper={true} {...inputLabels}>
<InputContainer
id={inputId}
useCustomWrapper={true}
label={hideLabels ? undefined : name}
helpText={hideLabels ? undefined : notice}
>
<div
className={classNames('flex flex-col gap-2', {
'rounded-xl border border-neutral-100 p-4': isNestedType,
Expand Down Expand Up @@ -114,7 +119,7 @@ export const ProposalActionsDecoderField: React.FC<IProposalActionsDecoderField>
onClick={handleAddArrayItem}
className={classNames('self-start')}
>
Add
{copy.proposalActionsDecoder.add}
</Button>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ export const ProposalActionsDecoderTextField: React.FC<IProposalActionsDecoderTe

const inputLabels = !hideLabels ? { label: name, helpText: notice } : undefined;
const commonProps = { placeholder: type, className, ...inputLabels };
const fieldProps = { parameter, fieldName: proposalActionsDecoderUtils.getFieldName(fieldName, formPrefix) };
const fieldProps = {
parameter,
component,
fieldName: proposalActionsDecoderUtils.getFieldName(fieldName, formPrefix),
};

if (mode === ProposalActionsDecoderMode.WATCH) {
return <ProposalActionsDecoderTextFieldWatch {...commonProps} {...fieldProps} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const ProposalActionsDecoderTextFieldEdit: React.FC<IProposalActionsDecod

const { copy } = useGukModulesContext();

const errorMessages = copy.proposalActionsItemFormField;
const errorMessages = copy.proposalActionsDecoder.validation;
const validationRulesParams = { label: name, type, required: true, errorMessages };
const validationRules = proposalActionsDecoderUtils.getValidationRules(validationRulesParams);

Expand All @@ -29,7 +29,5 @@ export const ProposalActionsDecoderTextFieldEdit: React.FC<IProposalActionsDecod

const Component = component === 'textarea' ? TextArea : InputText;

return (
<Component placeholder={type} value={field.value?.toString()} {...inputProps} {...fieldProps} {...otherProps} />
);
return <Component placeholder={type} value={value?.toString()} {...inputProps} {...fieldProps} {...otherProps} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export const ProposalActionsDecoderTextFieldWatch: React.FC<IProposalActionsDeco
const { type } = parameter;

const value = useWatch<Record<string, ProposalActionsFieldValue>>({ name: fieldName });

const Component = component === 'textarea' ? TextArea : InputText;

return <Component placeholder={type} value={value?.toString()} disabled={true} {...otherProps} />;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { DeepPartial } from 'react-hook-form';
import type { ModulesCopy } from '../../../../assets';
import { addressUtils } from '../../../../utils';
import type { IProposalActionInputDataParameter } from '../proposalActionsDefinitions';
import type { IProposalAction, IProposalActionInputDataParameter } from '../proposalActionsDefinitions';

export interface IGetValidationRulesParams {
/**
Expand All @@ -14,7 +15,7 @@ export interface IGetValidationRulesParams {
/**
* Strings to use for validation errors.
*/
errorMessages: ModulesCopy['proposalActionsItemFormField'];
errorMessages: ModulesCopy['proposalActionsDecoder']['validation'];
/**
* Defines if the field is required or not.
*/
Expand All @@ -38,7 +39,7 @@ class ProposalActionsDecoderUtils {
validateValue = (value: ProposalActionsFieldValue = null, params: IGetValidationRulesParams) => {
const { type, label, errorMessages } = params;

if (type === 'boolean') {
if (type === 'bool') {
return this.validateBoolean(value) || errorMessages.boolean(label);
} else if (type === 'address') {
return addressUtils.isAddress(value?.toString()) || errorMessages.address(label);
Expand All @@ -47,13 +48,12 @@ class ProposalActionsDecoderUtils {
return undefined;
};

valueSetter = (value: ProposalActionsFieldValue = null, type: string): ProposalActionsFieldValue => {
// Store value as boolean on form when valid, otherwise store it as lowercase string.
if (type === 'boolean') {
return this.validateBoolean(value) ? value === 'true' : value?.toString().toLocaleLowerCase();
}
formValuesToFunctionParameters = (formValues: DeepPartial<IProposalAction>): unknown[] | undefined => {
const values = formValues.inputData?.parameters?.map((parameter) =>
parameter?.type === 'bool' ? parameter.value === 'true' : parameter?.value,
);

return value;
return values;
};

isArrayType = (type: string) => type.endsWith('[]');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ export const Verified: Story = {
type: 'address',
value: '0x80CB2f4f9B403C4C418C597d96c95FE14FD344a6',
},
{
name: 'exact',
type: 'bool',
value: false,
},
],
},
}),
Expand Down

0 comments on commit bff61b3

Please sign in to comment.