Skip to content

Commit 2812556

Browse files
authored
Merge pull request #6543 from remix-project-org/improve_toaster
improve toaster
2 parents e1f794b + 7fffa2f commit 2812556

File tree

15 files changed

+671
-202
lines changed

15 files changed

+671
-202
lines changed

apps/remix-ide-e2e/src/commands/waitForElementContainsText.ts

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,75 @@
11
import { NightwatchBrowser } from 'nightwatch'
22
import EventEmitter from 'events'
33

4+
const findElementsAsync = (browser: NightwatchBrowser, selector: string): Promise<any[]> => {
5+
return new Promise((resolve, reject) => {
6+
browser.findElements(selector, (result) => {
7+
resolve(result.value as any)
8+
})
9+
})
10+
}
11+
12+
const getTextAsync = (browser: NightwatchBrowser, elementId: string): Promise<string> => {
13+
return new Promise((resolve, reject) => {
14+
browser.getText(elementId, (result) => {
15+
const text = typeof result === 'string' ? result : result.value
16+
resolve(text as any)
17+
})
18+
})
19+
}
20+
21+
422
class WaitForElementContainsText extends EventEmitter {
523
command (this: NightwatchBrowser, id: string, value: string, timeout = 10000): NightwatchBrowser {
624
let waitId // eslint-disable-line
7-
let currentValue
8-
const runid = setInterval(() => {
9-
this.api.getText(id, (result) => {
10-
currentValue = result.value
11-
if (typeof result.value === 'string' && result.value.indexOf(value) !== -1) {
25+
let currentValues: string[] = []
26+
const runid = setInterval(async () => {
27+
try {
28+
29+
let elements = await findElementsAsync(this.api, id)
30+
31+
if (!elements) {
32+
currentValues = []
33+
return
34+
}
35+
36+
if (elements.length === 0) {
37+
currentValues = []
38+
return
39+
}
40+
41+
// Check all elements that match the selector
42+
let foundMatch = false
43+
const textValues: string[] = []
44+
45+
for (const element of elements) {
46+
let text = await getTextAsync(this.api, element)
47+
currentValues.push(text)
48+
49+
if (typeof text === 'string' && text.indexOf(value) !== -1) {
50+
foundMatch = true
51+
break
52+
}
53+
}
54+
55+
currentValues = textValues
56+
57+
if (foundMatch) {
1258
clearInterval(runid)
1359
clearTimeout(waitId)
1460
this.api.assert.ok(true, `WaitForElementContainsText ${id} contains ${value}`)
1561
this.emit('complete')
1662
}
17-
})
63+
} catch (err) {
64+
// Ignore errors and continue polling
65+
console.error(`Error in waitForElementContainsText for selector ${id}:`, err)
66+
}
1867
}, 200)
1968

2069
waitId = setTimeout(() => {
2170
clearInterval(runid)
22-
this.api.assert.fail(`TimeoutError: An error occurred while running .waitForElementContainsText() command on ${id} after ${timeout} milliseconds. expected: ${value} - got: ${currentValue}`)
71+
const valuesFound = currentValues.length > 0 ? currentValues.join(', ') : 'none'
72+
this.api.assert.fail(`TimeoutError: An error occurred while running .waitForElementContainsText() command on ${id} after ${timeout} milliseconds. expected: ${value} - got: ${valuesFound}`)
2373
}, timeout)
2474
return this
2575
}

apps/remix-ide/src/app/plugins/notification.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,31 @@ import { LibraryProfile, MethodApi, StatusEvents } from '@remixproject/plugin-ut
44
import { AppModal } from '@remix-ui/app'
55
import { AlertModal } from '@remix-ui/app'
66
import { dispatchModalInterface } from '@remix-ui/app'
7+
import { Toaster, toast } from '@remix-ui/toaster'
78

89
interface INotificationApi {
910
events: StatusEvents
1011
methods: {
1112
modal: (args: AppModal) => void
1213
alert: (args: AlertModal) => void
13-
toast: (message: string) => void
14-
14+
toast: (message: string) => number
15+
hideToaster: (id: number) => void
1516
}
1617
}
1718

1819
const profile: LibraryProfile<INotificationApi> = {
1920
name: 'notification',
2021
displayName: 'Notification',
2122
description: 'Displays notifications',
22-
methods: ['modal', 'alert', 'toast']
23+
methods: ['modal', 'alert', 'toast', 'hideToaster']
2324
}
2425

2526
export class NotificationPlugin extends Plugin implements MethodApi<INotificationApi> {
2627
dispatcher: dispatchModalInterface
28+
toastId: number
2729
constructor() {
2830
super(profile)
31+
this.toastId = 0
2932
}
3033

3134
setDispatcher(dispatcher: dispatchModalInterface) {
@@ -40,7 +43,14 @@ export class NotificationPlugin extends Plugin implements MethodApi<INotificatio
4043
return this.dispatcher.alert(args)
4144
}
4245

43-
async toast(message: string | JSX.Element) {
44-
this.dispatcher.toast(message)
46+
async toast(message: string | JSX.Element, timeout?: number, timestamp?: number): Promise<number> {
47+
timestamp = timestamp || Date.now()
48+
timestamp = timestamp + ++this.toastId
49+
this.dispatcher.toast(message, timeout, timestamp)
50+
return timestamp
51+
}
52+
53+
async hideToaster(id: number) {
54+
toast.dismiss('toast-' + id)
4555
}
4656
}

apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5596,14 +5596,14 @@ textarea.form-control-lg {
55965596
--bs-toast-spacing: 1.5rem;
55975597
--bs-toast-max-width: 350px;
55985598
--bs-toast-font-size: 0.875rem;
5599-
--bs-toast-color: ;
5600-
--bs-toast-bg: #444;
5599+
--bs-toast-color: var(--bs-body-color);
5600+
--bs-toast-bg: var(--bs-light);
56015601
--bs-toast-border-width: var(--bs-border-width);
56025602
--bs-toast-border-color: var(--bs-border-color-translucent);
56035603
--bs-toast-border-radius: var(--bs-border-radius);
56045604
--bs-toast-box-shadow: var(--bs-box-shadow);
56055605
--bs-toast-header-color: var(--bs-secondary-color);
5606-
--bs-toast-header-bg: #303030;
5606+
--bs-toast-header-bg: var(--bs-body-bg);
56075607
--bs-toast-header-border-color: var(--bs-border-color-translucent);
56085608
width: var(--bs-toast-max-width);
56095609
max-width: 100%;

apps/remix-ide/src/assets/css/themes/remix-light_powaqg.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5598,14 +5598,14 @@ textarea.form-control-lg {
55985598
--bs-toast-spacing: 1.5rem;
55995599
--bs-toast-max-width: 350px;
56005600
--bs-toast-font-size: 0.875rem;
5601-
--bs-toast-color: ;
5602-
--bs-toast-bg: rgba(var(--bs-body-bg-rgb), 0.85);
5601+
--bs-toast-color: var(--bs-body-color);
5602+
--bs-toast-bg: var(--bs-light);
56035603
--bs-toast-border-width: var(--bs-border-width);
56045604
--bs-toast-border-color: var(--bs-border-color-translucent);
56055605
--bs-toast-border-radius: var(--bs-border-radius);
56065606
--bs-toast-box-shadow: var(--bs-box-shadow);
56075607
--bs-toast-header-color: var(--bs-secondary-color);
5608-
--bs-toast-header-bg: rgba(var(--bs-body-bg-rgb), 0.85);
5608+
--bs-toast-header-bg: var(--bs-body-bg);
56095609
--bs-toast-header-border-color: var(--bs-border-color-translucent);
56105610
width: var(--bs-toast-max-width);
56115611
max-width: 100%;

libs/remix-ui/app/src/lib/remix-app/actions/modals.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const enum modalActionTypes {
2323
type ModalPayload = {
2424
[modalActionTypes.setModal]: AppModal
2525
[modalActionTypes.handleHideModal]: any
26-
[modalActionTypes.setToast]: { message: string | JSX.Element, timestamp: number }
26+
[modalActionTypes.setToast]: { message: string | JSX.Element, timestamp: number, timeout?: number }
2727
[modalActionTypes.handleToaster]: any,
2828
[modalActionTypes.processQueue]: any,
2929
[modalActionTypes.setTemplateExplorer]: GenericModal

libs/remix-ui/app/src/lib/remix-app/components/modals/dialogs.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
1-
import React from 'react'
1+
import React, { useMemo } from 'react'
22
import { useDialogDispatchers, useDialogs } from '../../context/provider'
3-
import { Toaster } from '@remix-ui/toaster'
3+
import { ToasterContainer } from '@remix-ui/toaster'
44
import ModalWrapper from './modal-wrapper'
55

66
const AppDialogs = () => {
77
const { handleHideModal, handleToaster } = useDialogDispatchers()
8-
const { focusModal, focusToaster } = useDialogs()
8+
const { focusModal, toasters } = useDialogs()
9+
10+
// Map toasters to ToasterProps format with useMemo to prevent recreating on every render
11+
const toastList = useMemo(() => {
12+
return toasters.map((toaster) => ({
13+
message: toaster.message,
14+
id: toaster.toastId || `toast-${toaster.timestamp}`,
15+
timeout: toaster.timeout,
16+
timestamp: toaster.timestamp,
17+
handleHide: handleToaster
18+
}))
19+
}, [toasters, handleToaster])
920

1021
return (
1122
<>
1223
<ModalWrapper {...focusModal} handleHide={handleHideModal}></ModalWrapper>
13-
<Toaster message={focusToaster.message} timestamp={focusToaster.timestamp} handleHide={handleToaster} />
24+
<ToasterContainer toasts={toastList} />
1425
</>
1526
)
1627
}

libs/remix-ui/app/src/lib/remix-app/context/context.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ export const platformContext = React.createContext<appPlatformTypes>(null)
2626

2727
export interface dispatchModalInterface {
2828
modal: (data: AppModal) => void
29-
toast: (message: string | JSX.Element) => void
29+
toast: (message: string | JSX.Element, timeout?: number, toastId?: number) => void
3030
alert: (data: AlertModal) => void
3131
handleHideModal: () => void
3232
handleToaster: () => void
3333
}
3434

3535
export const dispatchModalContext = React.createContext<dispatchModalInterface>({
3636
modal: (data: AppModal) => {},
37-
toast: (message: string | JSX.Element) => {},
37+
toast: (message: string | JSX.Element, timeout?: number, toastId?: number) => {},
3838
alert: (data: AlertModal) => {},
3939
handleHideModal: () => {},
4040
handleToaster: () => {}

libs/remix-ui/app/src/lib/remix-app/context/provider.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,11 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt
6969
})
7070
}
7171

72-
const toast = (message: string | JSX.Element) => {
72+
const toast = (message: string | JSX.Element, timeout?: number, timestamp?: number) => {
73+
timestamp = timestamp || Date.now()
7374
dispatch({
7475
type: modalActionTypes.setToast,
75-
payload: { message, timestamp: Date.now() }
76+
payload: { message, timestamp, timeout }
7677
})
7778
}
7879

libs/remix-ui/app/src/lib/remix-app/interface/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ export interface AlertModal {
4040

4141
export interface ModalState {
4242
modals: AppModal[],
43-
toasters: {message: (string | JSX.Element), timestamp: number }[],
43+
toasters: {message: (string | JSX.Element), timestamp: number, timeout?: number, toastId?: number }[],
4444
focusModal: AppModal,
45-
focusToaster: {message: (string | JSX.Element), timestamp: number },
45+
focusToaster: {message: (string | JSX.Element), timestamp: number, timeout?: number, toastId?: number }
4646
focusTemplateExplorer: GenericModal
4747
}
4848

libs/remix-ui/app/src/lib/remix-app/state/modals.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const ModalInitialState: ModalState = {
1717
cancelFn: () => { },
1818
showCancelIcon: false
1919
},
20-
focusToaster: { message: '', timestamp: 0 },
20+
focusToaster: { message: '', timestamp: 0, timeout: 2000 },
2121
focusTemplateExplorer: {
2222
id: '',
2323
hide: true,

0 commit comments

Comments
 (0)