focus-trap 7.6.6 → 7.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +19 -6
- package/dist/focus-trap.esm.js +236 -36
- package/dist/focus-trap.esm.js.map +1 -1
- package/dist/focus-trap.esm.min.js +2 -2
- package/dist/focus-trap.esm.min.js.map +1 -1
- package/dist/focus-trap.js +236 -36
- package/dist/focus-trap.js.map +1 -1
- package/dist/focus-trap.min.js +2 -2
- package/dist/focus-trap.min.js.map +1 -1
- package/dist/focus-trap.umd.js +236 -36
- package/dist/focus-trap.umd.js.map +1 -1
- package/dist/focus-trap.umd.min.js +2 -2
- package/dist/focus-trap.umd.min.js.map +1 -1
- package/index.d.ts +10 -6
- package/index.js +194 -42
- package/package.json +32 -22
package/index.js
CHANGED
|
@@ -7,12 +7,20 @@ import {
|
|
|
7
7
|
} from 'tabbable';
|
|
8
8
|
|
|
9
9
|
const activeFocusTraps = {
|
|
10
|
+
// Returns the trap from the top of the stack.
|
|
11
|
+
getActiveTrap(trapStack) {
|
|
12
|
+
if (trapStack?.length > 0) {
|
|
13
|
+
return trapStack[trapStack.length - 1];
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
// Pauses the currently active trap, then adds a new trap to the stack.
|
|
10
19
|
activateTrap(trapStack, trap) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
20
|
+
const activeTrap = activeFocusTraps.getActiveTrap(trapStack);
|
|
21
|
+
|
|
22
|
+
if (trap !== activeTrap) {
|
|
23
|
+
activeFocusTraps.pauseTrap(trapStack);
|
|
16
24
|
}
|
|
17
25
|
|
|
18
26
|
const trapIndex = trapStack.indexOf(trap);
|
|
@@ -25,17 +33,28 @@ const activeFocusTraps = {
|
|
|
25
33
|
}
|
|
26
34
|
},
|
|
27
35
|
|
|
36
|
+
// Removes the trap from the top of the stack, then unpauses the next trap down.
|
|
28
37
|
deactivateTrap(trapStack, trap) {
|
|
29
38
|
const trapIndex = trapStack.indexOf(trap);
|
|
30
39
|
if (trapIndex !== -1) {
|
|
31
40
|
trapStack.splice(trapIndex, 1);
|
|
32
41
|
}
|
|
33
42
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
activeFocusTraps.unpauseTrap(trapStack);
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// Pauses the trap at the top of the stack.
|
|
47
|
+
pauseTrap(trapStack) {
|
|
48
|
+
const activeTrap = activeFocusTraps.getActiveTrap(trapStack);
|
|
49
|
+
activeTrap?._setPausedState(true);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
// Unpauses the trap at the top of the stack.
|
|
53
|
+
unpauseTrap(trapStack) {
|
|
54
|
+
const activeTrap = activeFocusTraps.getActiveTrap(trapStack);
|
|
55
|
+
|
|
56
|
+
if (activeTrap && !activeTrap._isManuallyPaused()) {
|
|
57
|
+
activeTrap._setPausedState(false);
|
|
39
58
|
}
|
|
40
59
|
},
|
|
41
60
|
};
|
|
@@ -109,6 +128,7 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
109
128
|
returnFocusOnDeactivate: true,
|
|
110
129
|
escapeDeactivates: true,
|
|
111
130
|
delayInitialFocus: true,
|
|
131
|
+
isolateSubtrees: false,
|
|
112
132
|
isKeyForward,
|
|
113
133
|
isKeyBackward,
|
|
114
134
|
...userOptions,
|
|
@@ -116,7 +136,7 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
116
136
|
|
|
117
137
|
const state = {
|
|
118
138
|
// containers given to createFocusTrap()
|
|
119
|
-
|
|
139
|
+
/** @type {Array<HTMLElement>} */
|
|
120
140
|
containers: [],
|
|
121
141
|
|
|
122
142
|
// list of objects identifying tabbable nodes in `containers` in the trap
|
|
@@ -124,17 +144,18 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
124
144
|
// is active, but the trap should never get to a state where there isn't at least one group
|
|
125
145
|
// with at least one tabbable node in it (that would lead to an error condition that would
|
|
126
146
|
// result in an error being thrown)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
147
|
+
/** @type {Array<{
|
|
148
|
+
* container: HTMLElement,
|
|
149
|
+
* tabbableNodes: Array<HTMLElement>, // empty if none
|
|
150
|
+
* focusableNodes: Array<HTMLElement>, // empty if none
|
|
151
|
+
* posTabIndexesFound: boolean,
|
|
152
|
+
* firstTabbableNode: HTMLElement|undefined,
|
|
153
|
+
* lastTabbableNode: HTMLElement|undefined,
|
|
154
|
+
* firstDomTabbableNode: HTMLElement|undefined,
|
|
155
|
+
* lastDomTabbableNode: HTMLElement|undefined,
|
|
156
|
+
* nextTabbableNode: (node: HTMLElement, forward: boolean) => HTMLElement|undefined
|
|
157
|
+
* }>}
|
|
158
|
+
*/
|
|
138
159
|
containerGroups: [], // same order/length as `containers` list
|
|
139
160
|
|
|
140
161
|
// references to objects in `containerGroups`, but only those that actually have
|
|
@@ -143,6 +164,13 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
143
164
|
// the same length
|
|
144
165
|
tabbableGroups: [],
|
|
145
166
|
|
|
167
|
+
// references to nodes that are siblings to the ancestors of this trap's containers.
|
|
168
|
+
/** @type {Set<HTMLElement>} */
|
|
169
|
+
adjacentElements: new Set(),
|
|
170
|
+
|
|
171
|
+
// references to nodes that were inert before the trap was activated.
|
|
172
|
+
/** @type {Set<HTMLElement>} */
|
|
173
|
+
alreadyInert: new Set(),
|
|
146
174
|
nodeFocusedBeforeActivation: null,
|
|
147
175
|
mostRecentlyFocusedNode: null,
|
|
148
176
|
active: false,
|
|
@@ -857,6 +885,64 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
857
885
|
return trap;
|
|
858
886
|
};
|
|
859
887
|
|
|
888
|
+
/**
|
|
889
|
+
* Traverses up the DOM from each of `containers`, collecting references to
|
|
890
|
+
* the elements that are siblings to `container` or an ancestor of `container`.
|
|
891
|
+
* @param {Array<HTMLElement>} containers
|
|
892
|
+
*/
|
|
893
|
+
const collectAdjacentElements = function (containers) {
|
|
894
|
+
// Re-activate all adjacent elements & clear previous collection.
|
|
895
|
+
if (state.active && !state.paused) {
|
|
896
|
+
trap._setSubtreeIsolation(false);
|
|
897
|
+
}
|
|
898
|
+
state.adjacentElements.clear();
|
|
899
|
+
state.alreadyInert.clear();
|
|
900
|
+
|
|
901
|
+
// Collect all ancestors of all containers to avoid redundant processing.
|
|
902
|
+
const containerAncestors = new Set();
|
|
903
|
+
|
|
904
|
+
const adjacentElements = new Set();
|
|
905
|
+
|
|
906
|
+
// Compile all elements adjacent to the focus trap containers & lineage.
|
|
907
|
+
for (const container of containers) {
|
|
908
|
+
containerAncestors.add(container);
|
|
909
|
+
let insideShadowRoot =
|
|
910
|
+
typeof ShadowRoot !== 'undefined' &&
|
|
911
|
+
container.getRootNode() instanceof ShadowRoot;
|
|
912
|
+
let current = container;
|
|
913
|
+
while (current) {
|
|
914
|
+
containerAncestors.add(current);
|
|
915
|
+
|
|
916
|
+
let parent = current.parentElement;
|
|
917
|
+
let siblings = [];
|
|
918
|
+
if (parent) {
|
|
919
|
+
siblings = parent.children;
|
|
920
|
+
} else if (!parent && insideShadowRoot) {
|
|
921
|
+
siblings = current.getRootNode().children;
|
|
922
|
+
parent = current.getRootNode().host;
|
|
923
|
+
insideShadowRoot =
|
|
924
|
+
typeof ShadowRoot !== 'undefined' &&
|
|
925
|
+
parent.getRootNode() instanceof ShadowRoot;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// Add all the children, we'll remove container lineage later.
|
|
929
|
+
for (const child of siblings) {
|
|
930
|
+
adjacentElements.add(child);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
current = parent;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Multi-container traps may overlap.
|
|
938
|
+
// Remove elements within container lineages.
|
|
939
|
+
containerAncestors.forEach((el) => {
|
|
940
|
+
adjacentElements.delete(el);
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
state.adjacentElements = adjacentElements;
|
|
944
|
+
};
|
|
945
|
+
|
|
860
946
|
const removeListeners = function () {
|
|
861
947
|
if (!state.active) {
|
|
862
948
|
return;
|
|
@@ -936,34 +1022,58 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
936
1022
|
const onPostActivate = getOption(activateOptions, 'onPostActivate');
|
|
937
1023
|
const checkCanFocusTrap = getOption(activateOptions, 'checkCanFocusTrap');
|
|
938
1024
|
|
|
939
|
-
|
|
940
|
-
|
|
1025
|
+
// If a currently-active trap is isolating its subtree, we need to remove
|
|
1026
|
+
// that isolation to allow the new trap to find tabbable nodes.
|
|
1027
|
+
const preexistingTrap = activeFocusTraps.getActiveTrap(trapStack);
|
|
1028
|
+
let revertState = false;
|
|
1029
|
+
if (preexistingTrap && !preexistingTrap.paused) {
|
|
1030
|
+
preexistingTrap._setSubtreeIsolation(false);
|
|
1031
|
+
revertState = true;
|
|
941
1032
|
}
|
|
942
1033
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1034
|
+
try {
|
|
1035
|
+
if (!checkCanFocusTrap) {
|
|
1036
|
+
updateTabbableNodes();
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
state.active = true;
|
|
1040
|
+
state.paused = false;
|
|
1041
|
+
state.nodeFocusedBeforeActivation = getActiveElement(doc);
|
|
1042
|
+
|
|
1043
|
+
onActivate?.();
|
|
946
1044
|
|
|
947
|
-
|
|
1045
|
+
const finishActivation = () => {
|
|
1046
|
+
if (checkCanFocusTrap) {
|
|
1047
|
+
updateTabbableNodes();
|
|
1048
|
+
}
|
|
1049
|
+
addListeners();
|
|
1050
|
+
updateObservedNodes();
|
|
1051
|
+
if (config.isolateSubtrees) {
|
|
1052
|
+
trap._setSubtreeIsolation(true);
|
|
1053
|
+
}
|
|
1054
|
+
onPostActivate?.();
|
|
1055
|
+
};
|
|
948
1056
|
|
|
949
|
-
const finishActivation = () => {
|
|
950
1057
|
if (checkCanFocusTrap) {
|
|
951
|
-
|
|
1058
|
+
checkCanFocusTrap(state.containers.concat()).then(
|
|
1059
|
+
finishActivation,
|
|
1060
|
+
finishActivation
|
|
1061
|
+
);
|
|
1062
|
+
return this;
|
|
952
1063
|
}
|
|
953
|
-
addListeners();
|
|
954
|
-
updateObservedNodes();
|
|
955
|
-
onPostActivate?.();
|
|
956
|
-
};
|
|
957
1064
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1065
|
+
finishActivation();
|
|
1066
|
+
} catch (error) {
|
|
1067
|
+
// If our activation throws an exception and the stack hasn't changed,
|
|
1068
|
+
// we need to re-enable the prior trap's subtree isolation.
|
|
1069
|
+
if (
|
|
1070
|
+
preexistingTrap === activeFocusTraps.getActiveTrap(trapStack) &&
|
|
1071
|
+
revertState
|
|
1072
|
+
) {
|
|
1073
|
+
preexistingTrap._setSubtreeIsolation(true);
|
|
1074
|
+
}
|
|
1075
|
+
throw error;
|
|
964
1076
|
}
|
|
965
|
-
|
|
966
|
-
finishActivation();
|
|
967
1077
|
return this;
|
|
968
1078
|
},
|
|
969
1079
|
|
|
@@ -982,6 +1092,14 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
982
1092
|
clearTimeout(state.delayInitialFocusTimer); // noop if undefined
|
|
983
1093
|
state.delayInitialFocusTimer = undefined;
|
|
984
1094
|
|
|
1095
|
+
// Prior to removing this trap from the trapStack, we need to remove any applications of `inert`.
|
|
1096
|
+
// This allows the next trap down to update its tabbable nodes properly.
|
|
1097
|
+
//
|
|
1098
|
+
// If this trap is not top of the stack, don't change any current isolation.
|
|
1099
|
+
if (!state.paused) {
|
|
1100
|
+
trap._setSubtreeIsolation(false);
|
|
1101
|
+
}
|
|
1102
|
+
state.alreadyInert.clear();
|
|
985
1103
|
removeListeners();
|
|
986
1104
|
state.active = false;
|
|
987
1105
|
state.paused = false;
|
|
@@ -1051,8 +1169,16 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
1051
1169
|
typeof element === 'string' ? doc.querySelector(element) : element
|
|
1052
1170
|
);
|
|
1053
1171
|
|
|
1172
|
+
if (config.isolateSubtrees) {
|
|
1173
|
+
collectAdjacentElements(state.containers);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1054
1176
|
if (state.active) {
|
|
1055
1177
|
updateTabbableNodes();
|
|
1178
|
+
|
|
1179
|
+
if (config.isolateSubtrees && !state.paused) {
|
|
1180
|
+
trap._setSubtreeIsolation(true);
|
|
1181
|
+
}
|
|
1056
1182
|
}
|
|
1057
1183
|
|
|
1058
1184
|
updateObservedNodes();
|
|
@@ -1074,6 +1200,7 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
1074
1200
|
}
|
|
1075
1201
|
|
|
1076
1202
|
state.paused = paused;
|
|
1203
|
+
|
|
1077
1204
|
if (paused) {
|
|
1078
1205
|
const onPause = getOption(options, 'onPause');
|
|
1079
1206
|
const onPostPause = getOption(options, 'onPostPause');
|
|
@@ -1081,6 +1208,7 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
1081
1208
|
|
|
1082
1209
|
removeListeners();
|
|
1083
1210
|
updateObservedNodes();
|
|
1211
|
+
trap._setSubtreeIsolation(false);
|
|
1084
1212
|
|
|
1085
1213
|
onPostPause?.();
|
|
1086
1214
|
} else {
|
|
@@ -1089,6 +1217,7 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
1089
1217
|
|
|
1090
1218
|
onUnpause?.();
|
|
1091
1219
|
|
|
1220
|
+
trap._setSubtreeIsolation(true);
|
|
1092
1221
|
updateTabbableNodes();
|
|
1093
1222
|
addListeners();
|
|
1094
1223
|
updateObservedNodes();
|
|
@@ -1099,6 +1228,29 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
1099
1228
|
return this;
|
|
1100
1229
|
},
|
|
1101
1230
|
},
|
|
1231
|
+
_setSubtreeIsolation: {
|
|
1232
|
+
value(isEnabled) {
|
|
1233
|
+
if (config.isolateSubtrees) {
|
|
1234
|
+
state.adjacentElements.forEach((el) => {
|
|
1235
|
+
if (isEnabled) {
|
|
1236
|
+
// check both attribute and property to ensure initial state is captured
|
|
1237
|
+
// correctly across different browsers and test environments (like JSDOM)
|
|
1238
|
+
const isInitiallyInert = el.inert || el.hasAttribute('inert');
|
|
1239
|
+
if (isInitiallyInert) {
|
|
1240
|
+
state.alreadyInert.add(el);
|
|
1241
|
+
}
|
|
1242
|
+
el.inert = true;
|
|
1243
|
+
} else {
|
|
1244
|
+
if (state.alreadyInert.has(el)) {
|
|
1245
|
+
// do nothing
|
|
1246
|
+
} else {
|
|
1247
|
+
el.inert = false;
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
},
|
|
1253
|
+
},
|
|
1102
1254
|
});
|
|
1103
1255
|
|
|
1104
1256
|
// initialize container elements
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "focus-trap",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.7.1",
|
|
4
4
|
"description": "Trap focus within a DOM node.",
|
|
5
5
|
"main": "dist/focus-trap.js",
|
|
6
6
|
"module": "dist/focus-trap.esm.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"format": "prettier --write \"{*,src/**/*,test/**/*,docs/js/**/*,.github/workflows/**/*,cypress/**/*}.+(js|cjs|mjs|ts|cts|mts|yml)\"",
|
|
22
22
|
"format:check": "prettier --check \"{*,src/**/*,test/**/*,docs/js/**/*,.github/workflows/**/*,cypress/**/*}.+(js|cjs|mjs|ts|cts|mts|yml)\"",
|
|
23
23
|
"format:watch": "onchange \"{*,src/**/*,test/**/*,docs/js/**/*,.github/workflows/**/*,cypress/**/*}.+(js|cjs|mjs|ts|cts|mts|yml)\" -- prettier --write {{changed}}",
|
|
24
|
-
"lint": "eslint \"{*,docs/js/**/*,cypress/e2e/**/*}.+(js|cjs|mjs|ts|cts|mts)\"",
|
|
24
|
+
"lint": "eslint \"{*,docs/js/**/*,cypress/e2e/**/*,test/**/*}.+(js|cjs|mjs|ts|cts|mts)\"",
|
|
25
25
|
"clean": "rm -rf ./dist",
|
|
26
26
|
"compile:esm": "cross-env BUILD_ENV=esm BABEL_ENV=esm rollup -c",
|
|
27
27
|
"compile:cjs": "cross-env BUILD_ENV=cjs BABEL_ENV=es5 rollup -c",
|
|
@@ -32,11 +32,12 @@
|
|
|
32
32
|
"start": "npm run compile:demo -- --watch --environment SERVE,RELOAD",
|
|
33
33
|
"start:cypress": "npm run compile:demo -- --environment SERVE,IS_CYPRESS_ENV:\"$CYPRESS_BROWSER\"",
|
|
34
34
|
"test:types": "tsc index.d.ts",
|
|
35
|
-
"test:unit": "
|
|
35
|
+
"test:unit": "jest",
|
|
36
|
+
"test:coverage": "jest --coverage",
|
|
36
37
|
"test:e2e": "ELECTRON_ENABLE_LOGGING=1 start-server-and-test start:cypress 9966 'cypress run --browser $CYPRESS_BROWSER --headless'",
|
|
37
38
|
"test:e2e:chrome": "CYPRESS_BROWSER=chrome npm run test:e2e",
|
|
38
39
|
"test:e2e:dev": "ELECTRON_ENABLE_LOGGING=1 start-server-and-test start:cypress 9966 'cypress open'",
|
|
39
|
-
"test": "npm run format:check && npm run lint && npm run test:
|
|
40
|
+
"test": "npm run format:check && npm run lint && npm run test:coverage && npm run test:types && npm run test:e2e:chrome",
|
|
40
41
|
"prepare": "npm run build",
|
|
41
42
|
"prepublishOnly": "npm run test && npm run build",
|
|
42
43
|
"release": "npm run build && changeset publish",
|
|
@@ -64,46 +65,55 @@
|
|
|
64
65
|
},
|
|
65
66
|
"homepage": "https://github.com/focus-trap/focus-trap#readme",
|
|
66
67
|
"dependencies": {
|
|
67
|
-
"tabbable": "^6.
|
|
68
|
+
"tabbable": "^6.4.0"
|
|
68
69
|
},
|
|
69
70
|
"devDependencies": {
|
|
70
71
|
"@babel/cli": "^7.28.3",
|
|
71
|
-
"@babel/core": "^7.28.
|
|
72
|
-
"@babel/eslint-parser": "^7.28.
|
|
72
|
+
"@babel/core": "^7.28.5",
|
|
73
|
+
"@babel/eslint-parser": "^7.28.5",
|
|
73
74
|
"@babel/eslint-plugin": "^7.27.1",
|
|
74
|
-
"@babel/preset-env": "^7.28.
|
|
75
|
-
"@changesets/cli": "^2.29.
|
|
76
|
-
"@eslint/js": "^9.
|
|
75
|
+
"@babel/preset-env": "^7.28.5",
|
|
76
|
+
"@changesets/cli": "^2.29.8",
|
|
77
|
+
"@eslint/js": "^9.39.2",
|
|
77
78
|
"@rollup/plugin-babel": "^6.1.0",
|
|
78
|
-
"@rollup/plugin-commonjs": "^
|
|
79
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
79
80
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
80
|
-
"@rollup/plugin-replace": "^6.0.
|
|
81
|
+
"@rollup/plugin-replace": "^6.0.3",
|
|
81
82
|
"@rollup/plugin-terser": "^0.4.4",
|
|
82
83
|
"@testing-library/cypress": "^10.1.0",
|
|
84
|
+
"@testing-library/dom": "^10.4.1",
|
|
85
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
86
|
+
"@testing-library/user-event": "^14.6.1",
|
|
87
|
+
"@types/jest": "^30.0.0",
|
|
83
88
|
"@types/jquery": "^3.5.33",
|
|
84
|
-
"@
|
|
85
|
-
"@typescript-eslint/
|
|
89
|
+
"@types/node": "^25.0.3",
|
|
90
|
+
"@typescript-eslint/eslint-plugin": "^8.51.0",
|
|
91
|
+
"@typescript-eslint/parser": "^8.50.0",
|
|
86
92
|
"all-contributors-cli": "^6.26.1",
|
|
93
|
+
"babel-jest": "^30.1.2",
|
|
87
94
|
"babel-loader": "^10.0.0",
|
|
88
95
|
"cross-env": "^10.1.0",
|
|
89
|
-
"cypress": "^
|
|
96
|
+
"cypress": "^15.8.1",
|
|
90
97
|
"cypress-plugin-tab": "^1.0.5",
|
|
91
|
-
"eslint": "^9.
|
|
98
|
+
"eslint": "^9.39.2",
|
|
92
99
|
"eslint-config-prettier": "^10.1.8",
|
|
93
100
|
"eslint-import-resolver-node": "^0.3.9",
|
|
94
101
|
"eslint-import-resolver-typescript": "^4.4.4",
|
|
95
102
|
"eslint-plugin-cypress": "^5.2.0",
|
|
96
103
|
"eslint-plugin-import": "^2.32.0",
|
|
97
|
-
"eslint-plugin-jest": "^29.0
|
|
104
|
+
"eslint-plugin-jest": "^29.12.0",
|
|
98
105
|
"eslint-plugin-jest-dom": "^5.5.0",
|
|
99
|
-
"eslint-plugin-testing-library": "^7.
|
|
100
|
-
"globals": "^16.
|
|
106
|
+
"eslint-plugin-testing-library": "^7.15.3",
|
|
107
|
+
"globals": "^16.5.0",
|
|
108
|
+
"jest": "^30.2.0",
|
|
109
|
+
"jest-environment-jsdom": "^30.2.0",
|
|
110
|
+
"jest-watch-typeahead": "^3.0.1",
|
|
101
111
|
"onchange": "^7.1.0",
|
|
102
|
-
"prettier": "^3.
|
|
103
|
-
"rollup": "^4.
|
|
112
|
+
"prettier": "^3.7.4",
|
|
113
|
+
"rollup": "^4.54.0",
|
|
104
114
|
"rollup-plugin-livereload": "^2.0.5",
|
|
105
115
|
"rollup-plugin-serve": "^3.0.0",
|
|
106
|
-
"start-server-and-test": "^2.1.
|
|
116
|
+
"start-server-and-test": "^2.1.3",
|
|
107
117
|
"typescript": "^5.9.3"
|
|
108
118
|
}
|
|
109
119
|
}
|