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

fkhadra / react-toastify / 12362857581

16 Dec 2024 11:01PM UTC coverage: 81.398% (-7.6%) from 88.998%
12362857581

push

github

fkhadra
chore: bump dependencies

320 of 423 branches covered (75.65%)

Branch coverage included in aggregate %.

437 of 507 relevant lines covered (86.19%)

580.66 hits per line

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

79.7
/src/core/containerObserver.ts
1
import {
2
  Id,
3
  NotValidatedToastProps,
4
  OnChangeCallback,
5
  Toast,
6
  ToastContainerProps,
7
  ToastContent,
8
  ToastProps
9
} from '../types';
10
import { canBeRendered, getAutoCloseDelay, isNum, parseClassName, toToastItem } from '../utils';
11

12
type Notify = () => void;
13

14
export type ContainerObserver = ReturnType<typeof createContainerObserver>;
15

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

29
  const observe = (notify: Notify) => {
428✔
30
    listeners.add(notify);
428✔
31
    return () => listeners.delete(notify);
428✔
32
  };
33

34
  const notify = () => {
428✔
35
    snapshot = Array.from(toasts.values());
798✔
36
    listeners.forEach(cb => cb());
798✔
37
  };
38

39
  const shouldIgnoreToast = ({ containerId, toastId, updateId }: NotValidatedToastProps) => {
428✔
40
    const containerMismatch = containerId ? containerId !== id : id !== 1;
776!
41
    const isDuplicate = toasts.has(toastId) && updateId == null;
776✔
42

43
    return containerMismatch || isDuplicate;
776✔
44
  };
45

46
  const toggle = (v: boolean, id?: Id) => {
428✔
47
    toasts.forEach(t => {
32✔
48
      if (id == null || id === t.props.toastId) t.toggle?.(v);
32✔
49
    });
50
  };
51

52
  const markAsRemoved = (v: Toast) => {
428✔
53
    v.props?.onClose?.(v.removalReason);
22✔
54
    v.isActive = false;
22✔
55
  };
56

57
  const removeToast = (id?: Id) => {
428✔
58
    if (id == null) {
22✔
59
      toasts.forEach(markAsRemoved);
×
60
    } else {
61
      const t = toasts.get(id);
22✔
62
      if (t) markAsRemoved(t);
22✔
63
    }
64
    notify();
22✔
65
  };
66

67
  const clearQueue = () => {
428✔
68
    toastCount -= queue.length;
×
69
    queue = [];
×
70
  };
71

72
  const addActiveToast = (toast: Toast) => {
428✔
73
    const { toastId, updateId } = toast.props;
776✔
74
    const isNew = updateId == null;
776✔
75

76
    if (toast.staleId) toasts.delete(toast.staleId);
776✔
77
    toast.isActive = true;
776✔
78

79
    toasts.set(toastId, toast);
776✔
80
    notify();
776✔
81
    dispatchChanges(toToastItem(toast, isNew ? 'added' : 'updated'));
776✔
82

83
    if (isNew) toast.props.onOpen?.();
776✔
84
  };
85

86
  const buildToast = <TData = unknown>(content: ToastContent<TData>, options: NotValidatedToastProps) => {
428✔
87
    if (shouldIgnoreToast(options)) return;
776!
88

89
    const { toastId, updateId, data, staleId, delay } = options;
776✔
90

91
    const isNotAnUpdate = updateId == null;
776✔
92

93
    if (isNotAnUpdate) toastCount++;
776✔
94

95
    const toastProps = {
776✔
96
      ...props,
97
      style: props.toastStyle,
98
      key: toastKey++,
99
      ...Object.fromEntries(Object.entries(options).filter(([_, v]) => v != null)),
5,082✔
100
      toastId,
101
      updateId,
102
      data,
103
      isIn: false,
104
      className: parseClassName(options.className || props.toastClassName),
758✔
105
      progressClassName: parseClassName(options.progressClassName || props.progressClassName),
763✔
106
      autoClose: options.isLoading ? false : getAutoCloseDelay(options.autoClose, props.autoClose),
388✔
107
      closeToast(reason?: true) {
108
        toasts.get(toastId)!.removalReason = reason;
6✔
109
        removeToast(toastId);
6✔
110
      },
111
      deleteToast() {
112
        const toastToRemove = toasts.get(toastId);
×
113

114
        if (toastToRemove == null) return;
×
115

116
        dispatchChanges(toToastItem(toastToRemove, 'removed'));
×
117
        toasts.delete(toastId);
×
118

119
        toastCount--;
×
120
        if (toastCount < 0) toastCount = 0;
×
121

122
        if (queue.length > 0) {
×
123
          addActiveToast(queue.shift());
×
124
          return;
×
125
        }
126

127
        notify();
×
128
      }
129
    } as ToastProps;
130

131
    toastProps.closeButton = props.closeButton;
776✔
132

133
    if (options.closeButton === false || canBeRendered(options.closeButton)) {
776✔
134
      toastProps.closeButton = options.closeButton;
38✔
135
    } else if (options.closeButton === true) {
738✔
136
      toastProps.closeButton = canBeRendered(props.closeButton) ? props.closeButton : true;
92!
137
    }
138

139
    const activeToast = {
776✔
140
      content,
141
      props: toastProps,
142
      staleId
143
    } as Toast;
144

145
    // not handling limit + delay by design. Waiting for user feedback first
146
    if (props.limit && props.limit > 0 && toastCount > props.limit && isNotAnUpdate) {
776!
147
      queue.push(activeToast);
×
148
    } else if (isNum(delay)) {
776✔
149
      setTimeout(() => {
130✔
150
        addActiveToast(activeToast);
130✔
151
      }, delay);
152
    } else {
153
      addActiveToast(activeToast);
646✔
154
    }
155
  };
156

157
  return {
428✔
158
    id,
159
    props,
160
    observe,
161
    toggle,
162
    removeToast,
163
    toasts,
164
    clearQueue,
165
    buildToast,
166
    setProps(p: ToastContainerProps) {
167
      props = p;
746✔
168
    },
169
    setToggle: (id: Id, fn: (v: boolean) => void) => {
170
      const t = toasts.get(id);
1,244✔
171
      if (t) t.toggle = fn;
1,244✔
172
    },
173
    isToastActive: (id: Id) => toasts.get(id)?.isActive,
820✔
174
    getSnapshot: () => snapshot
3,460✔
175
  };
176
}
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