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

fkhadra / react-toastify / 12246748306

10 Dec 2024 12:09AM UTC coverage: 81.078% (-7.9%) from 88.998%
12246748306

Pull #1178

github

fkhadra
chore: remove scss reference
Pull Request #1178: [WIP] V11

313 of 414 branches covered (75.6%)

Branch coverage included in aggregate %.

55 of 63 new or added lines in 14 files covered. (87.3%)

38 existing lines in 6 files now uncovered.

424 of 495 relevant lines covered (85.66%)

563.04 hits per line

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

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

14
type Notify = () => void;
15

16
export type ContainerObserver = ReturnType<typeof createContainerObserver>;
17

18
export function createContainerObserver(
19
  id: Id,
20
  containerProps: ToastContainerProps,
21
  dispatchChanges: OnChangeCallback
22
) {
23
  let toastKey = 1;
418✔
24
  let toastCount = 0;
418✔
25
  let queue: Toast[] = [];
418✔
26
  let snapshot: Toast[] = [];
418✔
27
  let props = containerProps;
418✔
28
  const toasts = new Map<Id, Toast>();
418✔
29
  const listeners = new Set<Notify>();
418✔
30

31
  const observe = (notify: Notify) => {
418✔
32
    listeners.add(notify);
418✔
33
    return () => listeners.delete(notify);
418✔
34
  };
35

36
  const notify = () => {
418✔
37
    snapshot = Array.from(toasts.values());
782✔
38
    listeners.forEach(cb => cb());
782✔
39
  };
40

41
  const shouldIgnoreToast = ({ containerId, toastId, updateId }: NotValidatedToastProps) => {
418✔
42
    const containerMismatch = containerId ? containerId !== id : id !== 1;
766!
43
    const isDuplicate = toasts.has(toastId) && updateId == null;
766✔
44

45
    return containerMismatch || isDuplicate;
766✔
46
  };
47

48
  const toggle = (v: boolean, id?: Id) => {
418✔
49
    toasts.forEach(t => {
28✔
50
      if (id == null || id === t.props.toastId) t.toggle?.(v);
28✔
51
    });
52
  };
53

54
  const markAsRemoved = (v: Toast) => {
418✔
55
    v.props?.onClose?.(v.removedByUser);
16✔
56
    v.isActive = false;
16✔
57
  };
58

59
  const removeToast = (id?: Id) => {
418✔
60
    if (id == null) {
16✔
NEW
61
      toasts.forEach(markAsRemoved);
×
62
    } else {
63
      const t = toasts.get(id);
16✔
64
      if (t) markAsRemoved(t);
16✔
65
    }
66
    notify();
16✔
67
  };
68

69
  const clearQueue = () => {
418✔
UNCOV
70
    toastCount -= queue.length;
×
UNCOV
71
    queue = [];
×
72
  };
73

74
  const addActiveToast = (toast: Toast) => {
418✔
75
    const { toastId, updateId } = toast.props;
766✔
76
    const isNew = updateId == null;
766✔
77

78
    if (toast.staleId) toasts.delete(toast.staleId);
766✔
79
    toast.isActive = true;
766✔
80

81
    toasts.set(toastId, toast);
766✔
82
    notify();
766✔
83
    dispatchChanges(toToastItem(toast, isNew ? 'added' : 'updated'));
766✔
84

85
    if (isNew) toast.props.onOpen?.();
766✔
86
  };
87

88
  const buildToast = <TData = unknown>(content: ToastContent<TData>, options: NotValidatedToastProps) => {
418✔
89
    if (shouldIgnoreToast(options)) return;
766!
90

91
    const { toastId, updateId, data, staleId, delay } = options;
766✔
92
    const closeToast = (removedByUser?: true) => {
766✔
NEW
93
      toasts.get(toastId)!.removedByUser = removedByUser;
×
UNCOV
94
      removeToast(toastId);
×
95
    };
96

97
    const isNotAnUpdate = updateId == null;
766✔
98

99
    if (isNotAnUpdate) toastCount++;
766✔
100

101
    const toastProps = {
766✔
102
      ...props,
103
      style: props.toastStyle,
104
      key: toastKey++,
105
      ...Object.fromEntries(Object.entries(options).filter(([_, v]) => v != null)),
4,948✔
106
      toastId,
107
      updateId,
108
      data,
109
      closeToast,
110
      isIn: false,
111
      className: parseClassName(options.className || props.toastClassName),
748✔
112
      bodyClassName: parseClassName(options.bodyClassName || props.bodyClassName),
753✔
113
      progressClassName: parseClassName(options.progressClassName || props.progressClassName),
753✔
114
      autoClose: options.isLoading ? false : getAutoCloseDelay(options.autoClose, props.autoClose),
383✔
115
      deleteToast() {
NEW
116
        const toastToRemove = toasts.get(toastId);
×
117

NEW
118
        if (toastToRemove == null) return;
×
119

UNCOV
120
        dispatchChanges(toToastItem(toastToRemove, 'removed'));
×
UNCOV
121
        toasts.delete(toastId);
×
122

UNCOV
123
        toastCount--;
×
UNCOV
124
        if (toastCount < 0) toastCount = 0;
×
125

UNCOV
126
        if (queue.length > 0) {
×
NEW
127
          addActiveToast(queue.shift());
×
UNCOV
128
          return;
×
129
        }
130

UNCOV
131
        notify();
×
132
      }
133
    } as ToastProps;
134

135
    toastProps.closeButton = props.closeButton;
766✔
136

137
    if (options.closeButton === false || canBeRendered(options.closeButton)) {
766✔
138
      toastProps.closeButton = options.closeButton;
38✔
139
    } else if (options.closeButton === true) {
728✔
140
      toastProps.closeButton = canBeRendered(props.closeButton) ? props.closeButton : true;
92!
141
    }
142

143
    let toastContent = content;
766✔
144

145
    if (isValidElement(content) && !isStr(content.type)) {
766!
NEW
146
      toastContent = cloneElement<ToastContentProps>(content as ReactElement<any>, {
×
147
        closeToast,
148
        toastProps,
149
        data
150
      });
151
    } else if (isFn(content)) {
766✔
152
      toastContent = content({ closeToast, toastProps, data: data as TData });
38✔
153
    }
154

155
    const activeToast = {
766✔
156
      content: toastContent,
157
      props: toastProps,
158
      staleId
159
    } as Toast;
160

161
    // not handling limit + delay by design. Waiting for user feedback first
162
    if (props.limit && props.limit > 0 && toastCount > props.limit && isNotAnUpdate) {
766!
UNCOV
163
      queue.push(activeToast);
×
164
    } else if (isNum(delay)) {
766✔
165
      setTimeout(() => {
130✔
166
        addActiveToast(activeToast);
130✔
167
      }, delay);
168
    } else {
169
      addActiveToast(activeToast);
636✔
170
    }
171
  };
172

173
  return {
418✔
174
    id,
175
    props,
176
    observe,
177
    toggle,
178
    removeToast,
179
    toasts,
180
    clearQueue,
181
    buildToast,
182
    setProps(p: ToastContainerProps) {
183
      props = p;
716✔
184
    },
185
    setToggle: (id: Id, fn: (v: boolean) => void) => {
186
      const t = toasts.get(id);
1,214✔
187
      if (t) t.toggle = fn;
1,214✔
188
    },
189
    isToastActive: (id: Id) => toasts.get(id)?.isActive,
800✔
190
    getSnapshot: () => snapshot
3,348✔
191
  };
192
}
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

© 2026 Coveralls, Inc