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

fkhadra / react-toastify / 12001201729

25 Nov 2024 12:54AM UTC coverage: 42.537% (-46.5%) from 88.998%
12001201729

Pull #1178

github

fkhadra
bump coverall action
Pull Request #1178: [WIP] V11

141 of 375 branches covered (37.6%)

Branch coverage included in aggregate %.

3 of 5 new or added lines in 3 files covered. (60.0%)

214 existing lines in 9 files now uncovered.

201 of 429 relevant lines covered (46.85%)

176.21 hits per line

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

0.0
/src/core/containerObserver.ts
1
import { ReactElement, cloneElement, isValidElement } from 'react';
2
import {
3
  Id,
4
  NotValidatedToastProps,
5
  OnChangeCallback,
6
  Toast,
7
  ToastContainerProps,
8
  ToastContent,
9
  ToastProps
10
} from '../types';
11
import {
12
  canBeRendered,
13
  getAutoCloseDelay,
14
  isFn,
15
  isNum,
16
  isStr,
17
  parseClassName,
18
  toToastItem
19
} from '../utils';
20

21
interface QueuedToast {
22
  content: ToastContent<any>;
23
  props: ToastProps;
24
  staleId?: Id;
25
}
26

27
type Notify = () => void;
28

29
interface ActiveToast {
30
  content: ToastContent<any>;
31
  props: ToastProps;
32
  staleId?: Id;
33
}
34

35
export type ContainerObserver = ReturnType<typeof createContainerObserver>;
36

37
export function createContainerObserver(
38
  id: Id,
39
  containerProps: ToastContainerProps,
40
  dispatchChanges: OnChangeCallback
41
) {
UNCOV
42
  let toastKey = 1;
×
UNCOV
43
  let toastCount = 0;
×
UNCOV
44
  let queue: QueuedToast[] = [];
×
UNCOV
45
  let activeToasts: Id[] = [];
×
UNCOV
46
  let snapshot: Toast[] = [];
×
UNCOV
47
  let props = containerProps;
×
UNCOV
48
  const toasts = new Map<Id, Toast>();
×
UNCOV
49
  const listeners = new Set<Notify>();
×
50

UNCOV
51
  const observe = (notify: Notify) => {
×
UNCOV
52
    listeners.add(notify);
×
UNCOV
53
    return () => listeners.delete(notify);
×
54
  };
55

UNCOV
56
  const notify = () => {
×
UNCOV
57
    snapshot = Array.from(toasts.values());
×
UNCOV
58
    listeners.forEach(cb => cb());
×
59
  };
60

UNCOV
61
  const shouldIgnoreToast = ({
×
62
    containerId,
63
    toastId,
64
    updateId
65
  }: NotValidatedToastProps) => {
UNCOV
66
    const containerMismatch = containerId ? containerId !== id : id !== 1;
×
UNCOV
67
    const isDuplicate = toasts.has(toastId) && updateId == null;
×
68

UNCOV
69
    return containerMismatch || isDuplicate;
×
70
  };
71

UNCOV
72
  const toggle = (v: boolean, id?: Id) => {
×
UNCOV
73
    toasts.forEach(t => {
×
UNCOV
74
      if (id == null || id === t.props.toastId) isFn(t.toggle) && t.toggle(v);
×
75
    });
76
  };
77

UNCOV
78
  const removeToast = (id?: Id) => {
×
UNCOV
79
    activeToasts = id == null ? [] : activeToasts.filter(v => v !== id);
×
UNCOV
80
    notify();
×
81
  };
82

UNCOV
83
  const clearQueue = () => {
×
UNCOV
84
    toastCount -= queue.length;
×
UNCOV
85
    queue = [];
×
86
  };
87

UNCOV
88
  const addActiveToast = (toast: ActiveToast) => {
×
UNCOV
89
    const { toastId, onOpen, updateId, children } = toast.props;
×
UNCOV
90
    const isNew = updateId == null;
×
91

UNCOV
92
    if (toast.staleId) toasts.delete(toast.staleId);
×
93

UNCOV
94
    toasts.set(toastId, toast);
×
UNCOV
95
    activeToasts = [...activeToasts, toast.props.toastId].filter(
×
UNCOV
96
      v => v !== toast.staleId
×
97
    );
UNCOV
98
    notify();
×
UNCOV
99
    dispatchChanges(toToastItem(toast, isNew ? 'added' : 'updated'));
×
100

UNCOV
101
    if (isNew && isFn(onOpen))
×
UNCOV
102
      onOpen(isValidElement(children) && children.props);
×
103
  };
104

UNCOV
105
  const buildToast = <TData = unknown>(
×
106
    content: ToastContent<TData>,
107
    options: NotValidatedToastProps
108
  ) => {
UNCOV
109
    if (shouldIgnoreToast(options)) return;
×
110

UNCOV
111
    const { toastId, updateId, data, staleId, delay } = options;
×
UNCOV
112
    const closeToast = () => {
×
UNCOV
113
      removeToast(toastId);
×
114
    };
115

UNCOV
116
    const isNotAnUpdate = updateId == null;
×
117

UNCOV
118
    if (isNotAnUpdate) toastCount++;
×
119

UNCOV
120
    const toastProps = {
×
121
      ...props,
122
      style: props.toastStyle,
123
      key: toastKey++,
124
      ...Object.fromEntries(
UNCOV
125
        Object.entries(options).filter(([_, v]) => v != null)
×
126
      ),
127
      toastId,
128
      updateId,
129
      data,
130
      closeToast,
131
      isIn: false,
132
      className: parseClassName(options.className || props.toastClassName),
×
133
      bodyClassName: parseClassName(
134
        options.bodyClassName || props.bodyClassName
×
135
      ),
136
      progressClassName: parseClassName(
137
        options.progressClassName || props.progressClassName
×
138
      ),
139
      autoClose: options.isLoading
×
140
        ? false
141
        : getAutoCloseDelay(options.autoClose, props.autoClose),
142
      deleteToast() {
UNCOV
143
        const toastToRemove = toasts.get(toastId)!;
×
UNCOV
144
        const { onClose, children } = toastToRemove.props;
×
UNCOV
145
        if (isFn(onClose)) onClose(isValidElement(children) && children.props);
×
146

UNCOV
147
        dispatchChanges(toToastItem(toastToRemove, 'removed'));
×
UNCOV
148
        toasts.delete(toastId);
×
149

UNCOV
150
        toastCount--;
×
UNCOV
151
        if (toastCount < 0) toastCount = 0;
×
152

UNCOV
153
        if (queue.length > 0) {
×
UNCOV
154
          addActiveToast(queue.shift() as ActiveToast);
×
UNCOV
155
          return;
×
156
        }
157

UNCOV
158
        notify();
×
159
      }
160
    } as ToastProps;
161

UNCOV
162
    toastProps.closeButton = props.closeButton;
×
163

UNCOV
164
    if (options.closeButton === false || canBeRendered(options.closeButton)) {
×
UNCOV
165
      toastProps.closeButton = options.closeButton;
×
UNCOV
166
    } else if (options.closeButton === true) {
×
UNCOV
167
      toastProps.closeButton = canBeRendered(props.closeButton)
×
168
        ? props.closeButton
169
        : true;
170
    }
171

UNCOV
172
    let toastContent = content;
×
173

UNCOV
174
    if (isValidElement(content) && !isStr(content.type)) {
×
175
      toastContent = cloneElement(content as ReactElement, {
×
176
        closeToast,
177
        toastProps,
178
        data
179
      });
UNCOV
180
    } else if (isFn(content)) {
×
UNCOV
181
      toastContent = content({ closeToast, toastProps, data: data as TData });
×
182
    }
183

UNCOV
184
    const activeToast = {
×
185
      content: toastContent,
186
      props: toastProps,
187
      staleId
188
    };
189

190
    // not handling limit + delay by design. Waiting for user feedback first
UNCOV
191
    if (
×
192
      props.limit &&
×
193
      props.limit > 0 &&
194
      toastCount > props.limit &&
195
      isNotAnUpdate
196
    ) {
UNCOV
197
      queue.push(activeToast);
×
UNCOV
198
    } else if (isNum(delay)) {
×
UNCOV
199
      setTimeout(() => {
×
UNCOV
200
        addActiveToast(activeToast);
×
201
      }, delay);
202
    } else {
UNCOV
203
      addActiveToast(activeToast);
×
204
    }
205
  };
206

UNCOV
207
  return {
×
208
    id,
209
    props,
210
    observe,
211
    toggle,
212
    removeToast,
213
    toasts,
214
    clearQueue,
215
    buildToast,
216
    setProps(p: ToastContainerProps) {
UNCOV
217
      props = p;
×
218
    },
219
    setToggle: (id: Id, fn: (v: boolean) => void) => {
UNCOV
220
      toasts.get(id)!.toggle = fn;
×
221
    },
UNCOV
222
    isToastActive: (id: Id) => activeToasts.some(v => v === id),
×
UNCOV
223
    getSnapshot: () => snapshot
×
224
  };
225
}
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