• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

input-output-hk / lace / 7140619322

08 Dec 2023 11:09AM UTC coverage: 52.927% (-0.9%) from 53.839%
7140619322

push

github

548ce7
pczeglik-iohk
fix: lw-9273 handle successful confirmation window (#779)

1988 of 4674 branches covered (0.0%)

Branch coverage included in aggregate %.

2 of 6 new or added lines in 2 files covered. (33.33%)

133 existing lines in 12 files now uncovered.

4485 of 7556 relevant lines covered (59.36%)

56.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

51.28
/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx
1
/* eslint-disable no-magic-numbers */
2
import React from 'react';
10✔
3
import cn from 'classnames';
10✔
4
import { TransactionDetailAsset, TransactionMetadataProps, TxOutputInput, TxSummary } from './TransactionDetailAsset';
5
import { Ellipsis, toast } from '@lace/common';
10✔
6
import { Box } from '@lace/ui';
10✔
7
import { useTranslate } from '@src/ui/hooks';
10✔
8
import CopyToClipboard from 'react-copy-to-clipboard';
10✔
9
import type { ActivityStatus } from '../Activity';
10
import styles from './TransactionDetails.module.scss';
10✔
11
import { TransactionInputOutput } from './TransactionInputOutput';
10✔
12
import { TransactionFee } from './TransactionFee';
10✔
13
import { ActivityDetailHeader } from './ActivityDetailHeader';
10✔
14

15
// eslint-disable-next-line @typescript-eslint/no-explicit-any
16
const displayMetadataMsg = (value: any[]): string => value?.find((val: any) => val.hasOwnProperty('msg'))?.msg || '';
10!
17

18
export interface TransactionDetailsProps {
19
  hash?: string;
20
  name: string;
21
  status?: ActivityStatus;
22
  /**
23
   * Transaction generation date
24
   */
25
  includedDate?: string;
26
  /**
27
   * Transaction generation time
28
   */
29
  includedTime?: string;
30
  assets?: TransactionDetailAsset[];
31
  /**
32
   * Input address list
33
   */
34
  addrInputs?: TxOutputInput[];
35
  /**
36
   * Output address list
37
   */
38
  addrOutputs?: TxOutputInput[];
39
  /**
40
   * Transaction total output
41
   */
42
  totalOutput?: string;
43
  /**
44
   * Transaction fee
45
   */
46
  fee?: string;
47
  pools?: { name: string; ticker: string; id: string }[];
48
  /**
49
   * Transaction deposit
50
   */
51
  deposit?: string;
52
  /**
53
   * Transaction returned deposit
54
   */
55
  depositReclaim?: string;
56
  /**
57
   * Transaction metadata
58
   */
59
  metadata?: TransactionMetadataProps['metadata'];
60
  amountTransformer: (amount: string) => string;
61
  headerDescription?: string;
62
  txSummary?: TxSummary[];
63
  coinSymbol: string;
64
  tooltipContent?: string;
65
  addressToNameMap: Map<string, string>;
66
  isPopupView?: boolean;
67
  openExternalLink?: () => void;
68
  sendAnalyticsInputs?: () => void;
69
  sendAnalyticsOutputs?: () => void;
70
}
71

72
const TOAST_DEFAULT_DURATION = 3;
10✔
73

74
const CopiableHash = ({ hash, copiedText }: { hash: string; copiedText: string }) => (
10✔
UNCOV
75
  <CopyToClipboard text={hash}>
×
76
    <div
77
      onClick={() =>
UNCOV
78
        toast.notify({
×
79
          duration: TOAST_DEFAULT_DURATION,
80
          text: copiedText
81
        })
82
      }
83
    >
84
      {hash}
85
    </div>
86
  </CopyToClipboard>
87
);
88

89
// eslint-disable-next-line react/no-multi-comp,complexity
90
export const TransactionDetails = ({
10✔
91
  hash,
92
  name,
93
  status,
94
  headerDescription,
95
  includedDate = '-',
×
96
  includedTime = '-',
×
97
  fee = '-',
×
98
  deposit,
99
  depositReclaim,
100
  addrInputs,
101
  addrOutputs,
102
  metadata,
103
  amountTransformer,
104
  txSummary = [],
12✔
105
  coinSymbol,
106
  pools,
107
  addressToNameMap,
108
  isPopupView,
109
  openExternalLink,
110
  sendAnalyticsInputs,
111
  sendAnalyticsOutputs
112
}: TransactionDetailsProps): React.ReactElement => {
113
  const { t } = useTranslate();
24✔
114
  const isSending = status === 'sending';
24✔
115
  const isSuccess = status === 'success';
24✔
116

117
  const renderDepositValueSection = ({ value, label }: { value: string; label: string }) => (
24✔
UNCOV
118
    <div className={styles.details}>
×
119
      <div className={styles.title}>{label}</div>
120
      <div className={styles.detail}>
121
        <div className={styles.amount}>
122
          <span className={styles.ada}>{`${value} ${coinSymbol}`}</span>
123
          <span className={styles.fiat}>{amountTransformer(value)}</span>
124
        </div>
125
      </div>
126
    </div>
127
  );
128

129
  return (
24✔
130
    <div data-testid="transaction-detail" className={styles.content}>
131
      <ActivityDetailHeader name={name} description={headerDescription} />
132
      <div>
133
        <div className={styles.header} data-testid="tx-header">
134
          {t('package.core.activityDetails.header')}
135
        </div>
136
        <div className={styles.block}>
137
          <div data-testid="tx-hash" className={styles.hashContainer}>
138
            <div className={cn(styles.title, styles.labelWidth)}>
139
              <div className={styles.hashLabel}>{t('package.core.activityDetails.transactionHash')}</div>
140
            </div>
141
            <div
142
              data-testid="tx-hash-detail"
143
              className={cn(styles.detail, styles.hash, {
144
                [styles.txLink]: isSuccess
145
              })}
146
              onClick={openExternalLink}
147
            >
148
              <div>
149
                {isSending ? (
150
                  <CopiableHash hash={hash} copiedText={t('package.core.activityDetails.copiedToClipboard')} />
12!
151
                ) : (
152
                  hash
153
                )}
154
              </div>
155
            </div>
156
          </div>
157

158
          <h1 className={styles.summary}>{t('package.core.activityDetails.summary')}</h1>
159
          {pools?.length > 0 && (
48!
160
            <div className={styles.stakingInfo}>
161
              <div className={cn(styles.title, styles.poolsTitle)}>{t('package.core.activityDetails.pools')}</div>
162
              <div className={styles.poolsList}>
163
                {pools?.map((pool) => (
×
UNCOV
164
                  <div key={pool.id} className={styles.poolEntry}>
×
165
                    <div className={styles.poolHeading}>
166
                      {pool.name && (
×
167
                        <div data-testid="tx-pool-name" className={styles.detail}>
168
                          {pool.name}
169
                        </div>
170
                      )}
171
                      {pool.ticker && (
×
172
                        <div data-testid="tx-pool-ticker" className={cn(styles.detail, styles.lightLabel)}>
173
                          ({pool.ticker})
174
                        </div>
175
                      )}
176
                    </div>
177
                    {pool.id && (
×
178
                      <div data-testid="tx-pool-id" className={cn(styles.detail, styles.poolId, styles.lightLabel)}>
179
                        <Ellipsis text={pool.id} ellipsisInTheMiddle />
180
                      </div>
181
                    )}
182
                  </div>
183
                ))}
184
              </div>
185
            </div>
186
          )}
UNCOV
187
          {txSummary.map((summary, index) => (
×
188
            <div key={index.toString()} data-testid="tx-detail-bundle">
189
              <div className={styles.details}>
190
                <div className={styles.title}>{name}</div>
191
                <div data-testid="tx-sent-detail" className={styles.detail}>
192
                  {summary.assetList?.map((asset, i) => (
×
UNCOV
193
                    <div className={styles.amount} key={`asset${i}`}>
×
194
                      <span data-testid="tx-sent-detail-token">
195
                        {asset.amount} {asset.symbol}
196
                      </span>
197
                      {asset?.fiatBalance && (
×
198
                        <span className={styles.fiat} data-testid="tx-sent-detail-token-fiat">
199
                          {asset.fiatBalance}
200
                        </span>
201
                      )}
202
                    </div>
203
                  ))}
204
                  <div className={styles.amount}>
205
                    <span
206
                      className={styles.ada}
207
                      data-testid="tx-sent-detail-ada"
208
                    >{`${summary.amount} ${coinSymbol}`}</span>
209
                    <span className={styles.fiat} data-testid="tx-sent-detail-fiat">{`${amountTransformer(
210
                      summary.amount
211
                    )}`}</span>
212
                  </div>
213
                </div>
214
              </div>
215
              <div className={styles.details}>
216
                <div className={styles.title}>
217
                  {t(`package.core.activityDetails.${name.toLowerCase() === 'sent' ? 'to' : 'from'}`)}
×
218
                </div>
219
                <div>
220
                  {summary.addr.length > 1 && (
×
221
                    <div
222
                      data-testid="tx-to-detail-multiple-addresses"
223
                      className={cn(styles.detail, styles.detailTitle)}
224
                    >
225
                      {t('package.core.activityDetails.multipleAddresses')}
226
                    </div>
227
                  )}
228
                  {(summary.addr as string[]).map((addr) => {
UNCOV
229
                    const addrName = addressToNameMap?.get(addr);
×
230
                    const address = isPopupView ? (
×
231
                      <Ellipsis className={cn(styles.addr, styles.fiat)} text={addr} ellipsisInTheMiddle />
×
232
                    ) : (
233
                      <span className={cn(styles.addr, styles.fiat)}>{addr}</span>
234
                    );
UNCOV
235
                    return (
×
236
                      <div key={addr} data-testid="tx-to-detail" className={cn(styles.addr, styles.detail)}>
237
                        {addrName ? (
238
                          <div className={styles.amount}>
×
239
                            <span className={cn(styles.ada, styles.addrName)}>{addrName}</span>
240
                            {address}
241
                          </div>
242
                        ) : (
243
                          address
244
                        )}
245
                      </div>
246
                    );
247
                  })}
248
                </div>
249
              </div>
250
            </div>
251
          ))}
252
          <div className={styles.details}>
253
            <div className={styles.title}>{t('package.core.activityDetails.status')}</div>
254
            {status && (
12!
255
              <div data-testid="tx-status" className={styles.detail}>{`${status.charAt(0).toUpperCase()}${status.slice(
256
                1
257
              )}`}</div>
258
            )}
259
          </div>
260
          <div data-testid="tx-date" className={cn(styles.details, styles.timestampContainer)}>
261
            <div className={cn(styles.title, styles.timestamp)}>{t('package.core.activityDetails.timestamp')}</div>
262
            <div data-testid="tx-timestamp" className={styles.detail}>
263
              <span>{includedDate}</span>
264
              <span>&nbsp;{includedTime}</span>
265
            </div>
266
          </div>
267
          {fee && fee !== '-' && (
36✔
268
            <Box mb="$32">
269
              <TransactionFee fee={fee} amountTransformer={amountTransformer} coinSymbol={coinSymbol} />
270
            </Box>
271
          )}
272
          {deposit && renderDepositValueSection({ value: deposit, label: t('package.core.activityDetails.deposit') })}
12!
273
          {depositReclaim &&
12!
274
            renderDepositValueSection({
275
              value: depositReclaim,
276
              label: t('package.core.activityDetails.depositReclaim')
277
            })}
278
        </div>
279

280
        {addrInputs?.length > 0 && (
60!
281
          <TransactionInputOutput
282
            amountTransformer={amountTransformer}
283
            title={t('package.core.activityDetails.inputs')}
284
            testId="tx-inputs"
285
            list={addrInputs}
286
            translations={{
287
              address: t('package.core.activityDetails.address'),
288
              sent: t('package.core.activityDetails.sent')
289
            }}
290
            coinSymbol={coinSymbol}
291
            withSeparatorLine
292
            sendAnalytics={sendAnalyticsInputs}
293
          />
294
        )}
295
        {addrOutputs?.length > 0 && (
58!
296
          <TransactionInputOutput
297
            amountTransformer={amountTransformer}
298
            title={t('package.core.activityDetails.outputs')}
299
            testId="tx-outputs"
300
            list={addrOutputs}
301
            translations={{
302
              address: t('package.core.activityDetails.address'),
303
              sent: t('package.core.activityDetails.sent')
304
            }}
305
            coinSymbol={coinSymbol}
306
            sendAnalytics={sendAnalyticsOutputs}
307
          />
308
        )}
309
        {metadata?.length > 0 && (
50✔
310
          <div className={styles.metadataContainer}>
311
            <div className={styles.metadataLabel}>{t('package.core.activityDetails.metadata')}</div>
312
            <div className={styles.detail} data-testid="tx-metadata">
313
              {metadata?.map((item) => (
6!
314
                <div key={item.key} className={styles.detail} data-testid={`tx-metadata-row-${item.key}`}>
4✔
315
                  {/* there are two options here item.value could be an array or string, if it is an array format item.value using displayMetadataMsg, if not just use item.value */}
316
                  {Array.isArray(item.value) ? displayMetadataMsg(item.value) : item.value}
2!
317
                </div>
318
              ))}
319
            </div>
320
          </div>
321
        )}
322
      </div>
323
    </div>
324
  );
325
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc