Skip to content

Commit 6fdaf85

Browse files
authored
Added custom notification support (#21)
* add custom notification support * updates after review * add changelog
1 parent dfe2c35 commit 6fdaf85

37 files changed

+673
-150
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
- [Added custom notification support](https://github.com/multiversx/mx-sdk-dapp-core-ui/pull/21)
11+
1012
## [[0.0.0-alpha.1](https://github.com/multiversx/mx-sdk-dapp-core-ui/pull/16)] - 2025-01-20
1113

1214
- [Updated formatAmount component and removed dependencies](https://github.com/multiversx/mx-sdk-dapp-core-ui/pull/17)
13-
- [Updated formatAmount component](https://github.com/multiversx/mx-sdk-dapp-core-ui/pull/15)
15+
- [Updated formatAmount component](https://github.com/multiversx/mx-sdk-dapp-core-ui/pull/15)

src/assets/notification.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ function showNotification() {
6767
newToast.toastId = notificationId.toString();
6868
newToast.transactions = [{ hash: 'erd1...8ctr', status: 'success' }];
6969

70-
const transactionListElement = document.getElementById('transaction-list');
70+
const transactionListElement = document.getElementById('toast-list');
7171
if (transactionListElement) {
7272
transactionListElement.appendChild(newToast);
7373
}

src/components.d.ts

Lines changed: 115 additions & 18 deletions
Large diffs are not rendered by default.

src/components/format-amount/format-amount.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { DataTestIdsEnum } from 'constants/dataTestIds.enum';
44
@Component({
55
tag: 'format-amount',
66
styleUrl: 'format-amount.css',
7-
shadow: true
7+
shadow: true,
88
})
99
export class FormatAmount {
1010
@Prop() class?: string;
@@ -15,15 +15,14 @@ export class FormatAmount {
1515
@Prop() valueDecimal: string;
1616
@Prop() valueInteger: string;
1717

18-
1918
render() {
2019
return this.isValid ? this.renderValid() : this.renderInvalid();
2120
}
2221

2322
private renderInvalid() {
2423
return (
2524
<span data-testid={this.dataTestId} class={this.class}>
26-
<span class='int-amount' data-testid={DataTestIdsEnum.formatAmountInt}>
25+
<span class="int-amount" data-testid={DataTestIdsEnum.formatAmountInt}>
2726
...
2827
</span>
2928
</span>
@@ -33,19 +32,19 @@ export class FormatAmount {
3332
private renderValid() {
3433
return (
3534
<span data-testid={this.dataTestId} class={this.class}>
36-
<span class='int-amount' data-testid={DataTestIdsEnum.formatAmountInt}>
35+
<span class="int-amount" data-testid={DataTestIdsEnum.formatAmountInt}>
3736
{this.valueInteger}
3837
</span>
3938
{this.valueDecimal && (
40-
<span class='decimals' data-testid={DataTestIdsEnum.formatAmountDecimals}>
39+
<span class="decimals" data-testid={DataTestIdsEnum.formatAmountDecimals}>
4140
.{this.valueDecimal}
4241
</span>
4342
)}
4443
{this.label && (
4544
<span
4645
class={{
47-
'symbol': true,
48-
[this.labelClass]: Boolean(this.labelClass)
46+
symbol: true,
47+
[this.labelClass]: Boolean(this.labelClass),
4948
}}
5049
data-testid={DataTestIdsEnum.formatAmountSymbol}
5150
>
@@ -55,5 +54,4 @@ export class FormatAmount {
5554
</span>
5655
);
5756
}
58-
5957
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
.toast-wrapper {
2+
border-radius: 0.25rem;
3+
box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);
4+
margin-bottom: 0.7rem;
5+
padding: 0.5rem;
6+
background-color: #fff;
7+
position: relative;
8+
}
9+
10+
.icon-close {
11+
position: absolute;
12+
right: 16px;
13+
top: 16px;
14+
cursor: pointer;
15+
font-size: 1.5rem;
16+
font-weight: 700;
17+
line-height: 1;
18+
color: #000;
19+
text-shadow: 0 1px 0 #fff;
20+
opacity: 0.5;
21+
padding: 0;
22+
background-color: rgba(0, 0, 0, 0);
23+
border: 0;
24+
display: flex;
25+
align-items: center;
26+
margin: 0 0.5rem;
27+
width: 12px;
28+
}
29+
30+
.toast-body {
31+
display: flex;
32+
align-items: center;
33+
flex: 1;
34+
font-size: 16px;
35+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { faTimes } from '@fortawesome/free-solid-svg-icons';
2+
import { Component, Prop, Event, h, EventEmitter } from '@stencil/core';
3+
import { IComponentToast } from 'components/toasts-list/components/transaction-toast/transaction-toast.type';
4+
import { getIconHtmlFromIconDefinition } from 'utils/icons/getIconHtmlFromIconDefinition';
5+
6+
@Component({
7+
tag: 'custom-toast',
8+
styleUrl: 'custom-toast.css',
9+
shadow: true,
10+
})
11+
export class CustomToast {
12+
@Prop() toast: IComponentToast;
13+
@Event() handleDeleteToast: EventEmitter<string>;
14+
15+
render() {
16+
const customToast = (
17+
<div class="toast-wrapper">
18+
<button onClick={() => this.handleDeleteToast.emit()} type="button" class="icon-close" innerHTML={getIconHtmlFromIconDefinition(faTimes)}></button>
19+
<div class="toast-body" ref={container => this.initializeToast(container)}></div>
20+
</div>
21+
);
22+
return customToast;
23+
}
24+
25+
private initializeToast(container: HTMLElement) {
26+
if (!container || container.hasChildNodes()) {
27+
return;
28+
}
29+
const customElement = this.toast.instantiateToastElement();
30+
if (!customElement) {
31+
return;
32+
}
33+
container.appendChild(customElement);
34+
}
35+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# custom-create-toast
2+
3+
4+
5+
<!-- Auto Generated Below -->
6+
7+
8+
## Properties
9+
10+
| Property | Attribute | Description | Type | Default |
11+
| -------- | --------- | ----------- | ----------------- | ----------- |
12+
| `toast` | -- | | `IComponentToast` | `undefined` |
13+
14+
15+
## Events
16+
17+
| Event | Description | Type |
18+
| ------------------- | ----------- | --------------------- |
19+
| `handleDeleteToast` | | `CustomEvent<string>` |
20+
21+
22+
## Dependencies
23+
24+
### Used by
25+
26+
- [generic-toast](../..)
27+
28+
### Graph
29+
```mermaid
30+
graph TD;
31+
generic-toast --> custom-toast
32+
style custom-toast fill:#f9f,stroke:#333,stroke-width:4px
33+
```
34+
35+
----------------------------------------------
36+
37+
*Built with [StencilJS](https://stenciljs.com/)*
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# simple-toast
2+
3+
4+
5+
<!-- Auto Generated Below -->
6+
7+
8+
## Properties
9+
10+
| Property | Attribute | Description | Type | Default |
11+
| -------- | --------- | ----------- | -------------- | ----------- |
12+
| `toast` | -- | | `ISimpleToast` | `undefined` |
13+
14+
15+
## Events
16+
17+
| Event | Description | Type |
18+
| ------------------- | ----------- | ------------------- |
19+
| `handleDeleteToast` | | `CustomEvent<void>` |
20+
21+
22+
## Dependencies
23+
24+
### Used by
25+
26+
- [generic-toast](../..)
27+
28+
### Depends on
29+
30+
- [transaction-toast-wrapper](../../../transaction-toast/components/transaction-toast-wrapper)
31+
32+
### Graph
33+
```mermaid
34+
graph TD;
35+
simple-toast --> transaction-toast-wrapper
36+
generic-toast --> simple-toast
37+
style simple-toast fill:#f9f,stroke:#333,stroke-width:4px
38+
```
39+
40+
----------------------------------------------
41+
42+
*Built with [StencilJS](https://stenciljs.com/)*
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
.content {
2+
display: flex;
3+
align-items: center;
4+
}
5+
6+
.content-left {
7+
display: flex;
8+
align-items: center;
9+
justify-content: center;
10+
flex-direction: column;
11+
padding-left: 0.5rem;
12+
padding-right: 0.75rem;
13+
14+
&.no-icon {
15+
display: none;
16+
}
17+
}
18+
19+
.content-icon {
20+
display: flex;
21+
align-items: center;
22+
justify-content: center;
23+
width: 3rem;
24+
height: 3rem;
25+
border-radius: 50%;
26+
27+
svg {
28+
height: auto;
29+
width: 20px;
30+
31+
path {
32+
fill: white;
33+
}
34+
}
35+
}
36+
37+
.icon-close {
38+
position: absolute;
39+
right: 16px;
40+
top: 16px;
41+
font-size: 1.5rem;
42+
font-weight: 700;
43+
line-height: 1;
44+
color: #000;
45+
text-shadow: 0 1px 0 #fff;
46+
opacity: 0.5;
47+
padding: 0;
48+
background-color: rgba(0, 0, 0, 0);
49+
border: 0;
50+
display: flex;
51+
align-items: center;
52+
margin: 0 0.5rem;
53+
width: 12px;
54+
}
55+
56+
.content-right {
57+
display: flex;
58+
flex-direction: column;
59+
justify-content: center;
60+
width: 100%;
61+
}
62+
63+
.content-heading {
64+
display: flex;
65+
justify-content: space-between;
66+
align-items: center;
67+
margin-bottom: 0.25rem;
68+
69+
h5 {
70+
margin: 0;
71+
margin-bottom: 0.5rem;
72+
font-weight: 500;
73+
line-height: 1.2;
74+
font-size: 1.25rem;
75+
}
76+
}
77+
78+
.content-message {
79+
margin-top: 0.25rem;
80+
font-size: 1rem;
81+
&.no-margin {
82+
margin-top: 0rem;
83+
}
84+
}
85+
86+
.subtitle {
87+
font-size: 0.9rem;
88+
font-weight: 500;
89+
color: #6c757d;
90+
}
91+
92+
.success {
93+
background-color: #5cb85c;
94+
}
95+
96+
.warning {
97+
background-color: #ffc107;
98+
}
99+
100+
.danger {
101+
background-color: #d9534f;
102+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { faTimes } from '@fortawesome/free-solid-svg-icons';
2+
import { Component, EventEmitter, Prop, Event, h } from '@stencil/core';
3+
import classNames from 'classnames';
4+
import { ISimpleToast } from 'components/toasts-list/components/transaction-toast/transaction-toast.type';
5+
import { DataTestIdsEnum } from 'constants/dataTestIds.enum';
6+
import { getIconHtmlFromIconDefinition } from 'utils/icons/getIconHtmlFromIconDefinition';
7+
import { getIconHtmlFromIconName } from 'utils/icons/getIconHtmlFromIconName';
8+
9+
@Component({
10+
tag: 'simple-toast',
11+
styleUrl: 'simple-toast.css',
12+
shadow: true,
13+
})
14+
export class SimpleToast {
15+
@Prop() toast: ISimpleToast;
16+
@Event() handleDeleteToast: EventEmitter<void>;
17+
18+
render() {
19+
const { icon, iconClassName, title, message, subtitle } = this.toast;
20+
21+
let iconHtml = null;
22+
if (typeof icon === 'string') {
23+
iconHtml = getIconHtmlFromIconName(icon);
24+
}
25+
if (icon instanceof HTMLElement) {
26+
iconHtml = icon.outerHTML;
27+
}
28+
29+
return (
30+
<transaction-toast-wrapper wrapperId={`toast-${this.toast.toastId}`}>
31+
<div class="content" data-testid={DataTestIdsEnum.transactionToastContent}>
32+
{iconHtml && (
33+
<div class={classNames('content-left', { 'no-icon': !iconHtml })}>
34+
<div class={classNames('content-icon', iconClassName)} innerHTML={iconHtml}></div>
35+
</div>
36+
)}
37+
38+
<div class="content-right">
39+
{title && (
40+
<div class="content-heading">
41+
<h5 class="content-heading-title" data-testid={DataTestIdsEnum.transactionToastTitle}>
42+
{title}
43+
</h5>
44+
</div>
45+
)}
46+
47+
<button onClick={() => this.handleDeleteToast.emit()} type="button" class="icon-close" innerHTML={getIconHtmlFromIconDefinition(faTimes)}></button>
48+
49+
{subtitle && <div class="subtitle">{subtitle}</div>}
50+
{message && <div class={classNames('content-message', { 'no-margin': !title && !subtitle })}>{message}</div>}
51+
</div>
52+
</div>
53+
</transaction-toast-wrapper>
54+
);
55+
}
56+
}

0 commit comments

Comments
 (0)