Create Enum, Option & Struct from toRawType (#1277)

* Create enums from toRawType

* Render with children

* De-dup static rendering

* Cleanups

* Cater for structs

* Allow breaks for params labels
This commit is contained in:
Jaco Greeff 2019-06-06 13:58:40 +02:00 committed by GitHub
parent d9218985b7
commit b3e68e6089
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 310 additions and 31 deletions

View File

@ -0,0 +1,113 @@
// Copyright 2017-2019 @polkadot/ui-app authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Props as BaseProps, RawParam } from '../types';
import React from 'react';
import { Enum, TypeDef, createType, getTypeDef } from '@polkadot/types';
import { Dropdown } from '@polkadot/ui-app';
import Params from '../';
import Bare from './Bare';
import Static from './Static';
type Props = BaseProps;
type State = {
def: TypeDef | null,
options: Array<{ text: string, value: string }>,
sub: Array<TypeDef>,
type: string | null
};
export default class EnumParam extends React.PureComponent<Props, State> {
state: State = {
def: null,
options: [],
sub: [],
type: null
};
static getDerivedStateFromProps ({ type: { type } }: Props, prevState: State) {
if (prevState.type === type) {
return null;
}
const sub = getTypeDef(createType(type).toRawType()).sub as Array<TypeDef>;
const options = sub.map(({ name }) => ({
text: name,
value: name
}));
const def = prevState.def || sub[0];
return {
def,
options,
sub,
type
} as State;
}
render () {
const { className, defaultValue, isDisabled, isError, label, style, withLabel } = this.props;
if (isDisabled) {
return <Static {...this.props} />;
}
const { def, options } = this.state;
const initialValue = defaultValue && defaultValue.value
? defaultValue.value instanceof Enum
? defaultValue.value.type
: Object.keys(defaultValue.value)[0]
: defaultValue;
return (
<Bare
className={className}
style={style}
>
<Dropdown
className='large'
defaultValue={initialValue}
isDisabled={isDisabled}
isError={isError}
label={label}
options={options}
onChange={this.onChange}
withEllipsis
withLabel={withLabel}
/>
{def && (
<Params
onChange={this.onChangeParam}
params={[{ name: def.name, type: def }]}
/>
)}
</Bare>
);
}
private onChange = (value: string): void => {
const { sub } = this.state;
this.setState({
def: sub.find(({ name }) => name === value) || null
});
}
private onChangeParam = ([{ isValid, value }]: Array<RawParam>): void => {
const { onChange } = this.props;
const { def } = this.state;
if (def) {
onChange && onChange({
isValid,
value: {
[def.name as string]: value
}
});
}
}
}

View File

@ -0,0 +1,22 @@
// Copyright 2017-2019 @polkadot/ui-app authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Props } from '../types';
import React from 'react';
export default class Null extends React.PureComponent<Props> {
componentDidMount () {
const { onChange } = this.props;
onChange && onChange({
isValid: true,
value: null
});
}
render () {
return null;
}
}

View File

@ -0,0 +1,39 @@
// Copyright 2017-2019 @polkadot/ui-app authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { WithTranslation } from 'react-i18next';
import { Props as BareProps, RawParam } from '../types';
import React from 'react';
import { Static } from '@polkadot/ui-app';
import translate from '@polkadot/ui-app/translate';
import Bare from './Bare';
type Props = BareProps & WithTranslation & {
defaultValue: RawParam,
withLabel?: boolean
};
class StaticParam extends React.PureComponent<Props> {
render () {
const { className, defaultValue, label, style, t } = this.props;
const value = defaultValue && defaultValue.value && defaultValue.value.toString();
return (
<Bare
className={className}
style={style}
>
<Static
className='full'
label={label}
value={value || t('empty')}
/>
</Bare>
);
}
}
export default translate(StaticParam);

View File

@ -0,0 +1,85 @@
// Copyright 2017-2019 @polkadot/ui-app authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Props as BaseProps, RawParam } from '../types';
import React from 'react';
import { TypeDef, createType, getTypeDef } from '@polkadot/types';
import Params from '../';
import Base from './Base';
import Static from './Static';
type Props = BaseProps;
type State = {
defs: Array<TypeDef>,
type: string | null
};
export default class StructParam extends React.PureComponent<Props, State> {
state: State = {
defs: [],
type: null
};
static getDerivedStateFromProps ({ type: { type } }: Props, prevState: State) {
if (prevState.type === type) {
return null;
}
const defs = getTypeDef(createType(type).toRawType()).sub as Array<TypeDef>;
return {
defs,
type
} as State;
}
render () {
const { className, isDisabled, label, style, withLabel } = this.props;
if (isDisabled) {
return <Static {...this.props} />;
}
const { defs } = this.state;
const params = defs.map((type) => ({ name: type.name, type }));
return (
<div>
<Base
className={className}
label={label}
size='full'
style={style}
withLabel={withLabel}
>
&nbsp;
</Base>
<Params
onChange={this.onChangeParams}
params={params}
/>
</div>
);
}
private onChangeParams = (values: Array<RawParam>) => {
const { onChange } = this.props;
if (onChange) {
const { defs } = this.state;
onChange({
isValid: values.reduce((result, { isValid }) => result && isValid, true as boolean),
value: defs.reduce((value, { name }, index) => {
value[name as string] = values[index].value;
return value;
}, {} as { [index: string]: any })
});
}
}
}

View File

@ -2,40 +2,24 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { WithTranslation } from 'react-i18next';
import { Props as BareProps, RawParam } from '../types';
import React from 'react';
import { Static } from '@polkadot/ui-app';
import translate from '@polkadot/ui-app/translate';
import Bare from './Bare';
import BaseBytes from './BaseBytes';
import Static from './Static';
type Props = BareProps & WithTranslation & {
type Props = BareProps & {
defaultValue: RawParam,
withLabel?: boolean
};
class Unknown extends React.PureComponent<Props> {
export default class Unknown extends React.PureComponent<Props> {
render () {
const { className, defaultValue, isDisabled, isError, label, name, onChange, onEnter, style, t, type } = this.props;
const { className, defaultValue, isDisabled, isError, label, name, onChange, onEnter, style, type } = this.props;
if (isDisabled) {
const value = defaultValue && defaultValue.value && defaultValue.value.toString();
return (
<Bare
className={className}
style={style}
>
<Static
className='full'
label={label}
value={value || t('empty')}
/>
</Bare>
);
return <Static {...this.props} />;
}
return (
@ -57,5 +41,3 @@ class Unknown extends React.PureComponent<Props> {
);
}
}
export default translate(Unknown);

View File

@ -2,20 +2,25 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { TypeDef, TypeDefInfo, UInt, createType } from '@polkadot/types';
import { Props, ComponentMap } from '../types';
import BN from 'bn.js';
import { TypeDef, TypeDefInfo, createType, getTypeDef } from '@polkadot/types';
import Account from './Account';
import Amount from './Amount';
import Balance from './Balance';
import Bool from './Bool';
import Bytes from './Bytes';
import Code from './Code';
import Enum from './Enum';
import Hash from './Hash';
import Moment from './Moment';
import Proposal from './Proposal';
import KeyValue from './KeyValue';
import KeyValueArray from './KeyValueArray';
import Null from './Null';
import Struct from './Struct';
import Text from './Text';
import Tuple from './Tuple';
import Unknown from './Unknown';
@ -35,16 +40,20 @@ const components: ComponentMap = ([
{ c: Bool, t: ['bool'] },
{ c: Bytes, t: ['Bytes'] },
{ c: Code, t: ['Code'] },
{ c: Enum, t: ['Enum'] },
{ c: Hash, t: ['CodeHash', 'Hash', 'SeedOf', 'Signature'] },
{ c: KeyValue, t: ['KeyValue'] },
{ c: KeyValueArray, t: ['Vec<KeyValue>'] },
{ c: Moment, t: ['Moment', 'MomentOf'] },
{ c: Null, t: ['Null'] },
{ c: Proposal, t: ['Proposal'] },
{ c: Text, t: ['String', 'Text'] },
{ c: Struct, t: ['Struct'] },
{ c: Tuple, t: ['Tuple'] },
{ c: Vector, t: ['Vector'] },
{ c: Vote, t: ['Vote'] },
{ c: VoteThreshold, t: ['VoteThreshold'] }
{ c: VoteThreshold, t: ['VoteThreshold'] },
{ c: Unknown, t: ['Unknown'] }
] as Array<TypeToComponent>).reduce((components, { c, t }) => {
t.forEach((type) => {
components[type] = c;
@ -57,8 +66,15 @@ export default function findComponent (def: TypeDef, overrides: ComponentMap = {
const type = (({ info, sub, type }: TypeDef) => {
switch (info) {
case TypeDefInfo.Compact:
case TypeDefInfo.Option:
return (sub as TypeDef).type;
case TypeDefInfo.Enum:
return 'Enum';
case TypeDefInfo.Struct:
return 'Struct';
case TypeDefInfo.Tuple:
return 'Tuple';
@ -77,9 +93,12 @@ export default function findComponent (def: TypeDef, overrides: ComponentMap = {
if (!Component) {
try {
const instance = createType(type);
const raw = getTypeDef(instance.toRawType());
if (instance instanceof UInt) {
if (instance instanceof BN) {
return Amount;
} else if ([TypeDefInfo.Enum, TypeDefInfo.Struct].includes(raw.info)) {
return findComponent(raw, overrides);
}
} catch (error) {
// console.error(error.message);

View File

@ -17,6 +17,10 @@
.ui--Params-Content {
box-sizing: border-box;
padding: 0 0 0 1.75rem;
label .withEllipsis {
white-space: normal;
}
}
.ui--Param-text {

View File

@ -5,7 +5,7 @@
import { RawParam$Value } from './types';
import BN from 'bn.js';
import { Bytes, Hash, TypeDef, TypeDefInfo, U8a, UInt, createType } from '@polkadot/types';
import { Bytes, Hash, TypeDef, TypeDefInfo, U8a, createType, getTypeDef } from '@polkadot/types';
export default function getInitValue (def: TypeDef): RawParam$Value | Array<RawParam$Value> {
if (def.info === TypeDefInfo.Vector) {
@ -15,11 +15,20 @@ export default function getInitValue (def: TypeDef): RawParam$Value | Array<RawP
? def.sub.map((def) => getInitValue(def))
: [];
} else if (def.info === TypeDefInfo.Struct) {
console.error(`Unable to determine default type from Struct ${JSON.stringify(def)}`);
return void 0;
return Array.isArray(def.sub)
? def.sub.reduce((result, def) => {
result[def.name as string] = getInitValue(def);
return result;
}, {} as { [index: string]: RawParam$Value | Array<RawParam$Value> })
: {};
} else if (def.info === TypeDefInfo.Enum) {
return Array.isArray(def.sub)
? { [def.sub[0].name as string]: getInitValue(def.sub[0]) }
: {};
}
const type = def.info === TypeDefInfo.Compact
const type = [TypeDefInfo.Compact, TypeDefInfo.Option].includes(def.info)
? (def.sub as TypeDef).type
: def.type;
@ -84,12 +93,18 @@ export default function getInitValue (def: TypeDef): RawParam$Value | Array<RawP
case 'Extrinsic':
return new U8a();
case 'Null':
return null;
default: {
try {
const instance = createType(type);
const raw = getTypeDef(instance.toRawType());
if (instance instanceof UInt) {
if (instance instanceof BN) {
return new BN(0);
} else if ([TypeDefInfo.Enum, TypeDefInfo.Struct].includes(raw.info)) {
return getInitValue(raw);
}
} catch (error) {
// console.error(error.message);