Walletbeat UI development guide
Not complete yet!
Custom UI components for attributes
Most attributes can have basic details as Markdown. However, some attributes need more display richness to better explain why the wallet rating is what it is for that attribute. For example, it is useful to list the security audit history as part of the details of the “Security audits” attribute. This exceeds the rendering capabilities of Markdown, so such components should use a custom Svelte-based component which gives them full control over the rendering.
The pieces needed for custom components are as follows:
- Create a new component at
src/views/attributes/<attribute_group>/<YourAttributeId>Details.svelte. Seesrc/views/attributes/security/SecurityAuditsDetails.sveltefor an example. Your props must contain a prop namedwallet(type:RatedWallet) and a prop namedvalue(type: your attribute’sValuesubtype). You may also add any other props you like, but note that thevalueprop being present means your component has access to any fields defined in your attribute’sValuesubtype already.
// Props
const {
wallet,
value,
// (You may add more props here if you'd like! But you must have the above two props.)
}: {
wallet: RatedWallet
value: YourAttributeIdValue
} = $props()
- Create a content datatype that will act as the input to your component. See
src/types/content/security-audits-details.tsas an example:
import type { EvaluationData } from '@/schema/attributes'
// Your attribute's `Value` subtype:
import type { SecurityAuditsValue } from '@/schema/attributes/security/security-audits'
import { component, type Content } from '../content'
export interface SecurityAuditsDetailsProps extends EvaluationData<SecurityAuditsValue> {
// Any extra props needed by your Svelte component
// other than `wallet` and `value`:
auditedInLastYear: boolean
hasUnaddressedFlaws: boolean
}
// Boilerplate; adjust for your own attribute:
export interface SecurityAuditsDetailsContent {
component: 'SecurityAuditsDetails'
componentProps: SecurityAuditsDetailsProps
}
// Boilerplate; adjust for your own attribute:
export function securityAuditsDetailsContent(
bakedProps: Omit<SecurityAuditsDetailsProps, keyof EvaluationData<SecurityAuditsValue>>,
): Content<{ WALLET_NAME: string }> {
return component<SecurityAuditsDetailsContent, keyof typeof bakedProps>(
'SecurityAuditsDetails',
bakedProps,
)
}
- Edit
src/types/content.ts’sComponentAndPropsto add your own content type. In the example above, this would beSecurityAuditsDetailsContent. - Edit the
rating-content-classdivinsrc/views/WalletPage.svelte, where you will see something like this (add your own):
<div class="rating-content">
<!-- ... -->
<div data-column>
{#if componentName === 'AddressCorrelationDetails'}
<AddressCorrelationDetails {...componentProps} {wallet} {value} {references} />
{:else if componentName === 'ChainVerificationDetails'}
<ChainVerificationDetails {...componentProps} {wallet} {value} {references} />
{:else if componentName === 'ScamAlertDetails'}
<ScamAlertDetails {...componentProps} {wallet} {value} {references} />
{:else if componentName === 'SecurityAuditsDetails'}
<SecurityAuditsDetails {...componentProps} {wallet} {value} {references} />
{:else if componentName === 'TransactionInclusionDetails'}
<TransactionInclusionDetails {...componentProps} {wallet} {value} {references} />
{:else if componentName === 'FundingDetails'}
<FundingDetails {...componentProps} {wallet} {value} {references} />
{:else if componentName === 'AccountRecoveryDetails'}
<AccountRecoveryDetails {...componentProps} {wallet} {value} {references} />
{:else if componentName === 'UnratedAttribute'}
<UnratedAttribute {...componentProps} {wallet} {value} {references} />
{/if}
</div>
</div>
- Finally, in your attribute’s
Evaluations, use the<YourAttributeId>DetailsContent(...)function in thedetailsfield, passing in any fields you added in<YourAttributeId>DetailsProps. Example for the security audits case:
return {
value: {
id: `...`,
rating,
displayName,
shortExplanation,
securityAudits: audits,
},
details: securityAuditsDetailsContent({
// Props defined in SecurityAuditsDetailsProps:
auditedInLastYear,
hasUnaddressedFlaws,
}),
howToImprove,
references,
}
- Run
pnpm devand observe your new component!