From abd13d21ccbd90e376d9878b031f3b910f468f12 Mon Sep 17 00:00:00 2001
From: LiZhenhai-MBP14 <5935568+jackhai9@users.noreply.github.com>
Date: Thu, 27 Aug 2026 12:58:08 +0800
Subject: [PATCH] fix: gray out inactive ladder actions
---
.../specs/control-flows.pw.js | 49 +++++--------------
scripts/binance-orderbook-trade.user.js | 28 ++++-------
src/binance-orderbook-trade/index.user.js | 26 +++-------
.../ladder-options.test.js | 4 +-
.../source-regressions.test.js | 9 ++--
5 files changed, 36 insertions(+), 80 deletions(-)
diff --git a/e2e/binance-orderbook/specs/control-flows.pw.js b/e2e/binance-orderbook/specs/control-flows.pw.js
index 6a6a66f..8a4e353 100644
--- a/e2e/binance-orderbook/specs/control-flows.pw.js
+++ b/e2e/binance-orderbook/specs/control-flows.pw.js
@@ -23,6 +23,14 @@ async function readRect(locator) {
return rect;
}
+async function expectStandardDisabledStyle(locator) {
+ await expect(locator).toHaveCSS('background-color', 'rgb(245, 245, 245)');
+ await expect(locator).toHaveCSS('border-color', 'rgb(213, 217, 226)');
+ await expect(locator).toHaveCSS('color', 'rgb(183, 189, 198)');
+ await expect(locator).toHaveCSS('opacity', '0.65');
+ await expect(locator).toHaveCSS('cursor', 'not-allowed');
+}
+
test('native open and close tabs drive one stable panel direction selector', async ({ page }) => {
const scenario = createCancelScenario({
positions: POSITION_SETS.current,
@@ -156,31 +164,16 @@ test('starting a ladder preserves action slots and turns only the active action
await expect(stop).toHaveCount(0);
const startLongRect = await readRect(startLong);
const startShortRect = await readRect(startShort);
- const startShortTone = await startShort.evaluate((button) => {
- const style = getComputedStyle(button);
- return {
- backgroundColor: style.backgroundColor,
- borderColor: style.borderColor,
- color: style.color,
- };
- });
const cancelRect = await readRect(cancel);
await installInteractionProbe(page, '[data-ladder-action="OPEN_LONG"]');
await startLong.click();
await expect(startLong).toHaveCount(0);
await expect(startShort).toBeDisabled();
await expect(stop).toBeEnabled();
+ await expectStandardDisabledStyle(startShort);
const stopRect = await readRect(stop);
expect(stopRect).toEqual(startLongRect);
expect(await readRect(startShort)).toEqual(startShortRect);
- expect(await startShort.evaluate((button) => {
- const style = getComputedStyle(button);
- return {
- backgroundColor: style.backgroundColor,
- borderColor: style.borderColor,
- color: style.color,
- };
- })).toEqual(startShortTone);
expect(await readRect(cancel)).toEqual(cancelRect);
const submissionsBeforeStop = (await readFixtureState(page)).events
.filter((event) => event.type === 'order-submitted').length;
@@ -213,16 +206,16 @@ test('starting close short keeps unavailable close long in the standard disabled
const closeShort = panel.getByRole('button', { name: '阶梯平空' });
await expect(closeLong).toBeDisabled();
- await expect(closeLong).not.toHaveAttribute('data-ladder-preserve-tone', 'true');
+ await expectStandardDisabledStyle(closeLong);
await expect(closeShort).toBeEnabled();
await closeShort.click();
await expect(panel.getByRole('button', { name: '停止平空' })).toBeEnabled();
await expect(closeLong).toBeDisabled();
- await expect(closeLong).not.toHaveAttribute('data-ladder-preserve-tone', 'true');
+ await expectStandardDisabledStyle(closeLong);
expect(errors).toEqual([]);
});
-test('starting close short preserves close long tone only when a long position exists', async ({ page }) => {
+test('starting close short disables close long with the standard disabled style', async ({ page }) => {
const scenario = createCancelScenario({
positions: [
{ symbol: CURRENT_SYMBOL, side: 'LONG', quantity: '0.1' },
@@ -237,26 +230,10 @@ test('starting close short preserves close long tone only when a long position e
await expect(closeLong).toBeEnabled();
await expect(closeShort).toBeEnabled();
- const closeLongTone = await closeLong.evaluate((button) => {
- const style = getComputedStyle(button);
- return {
- backgroundColor: style.backgroundColor,
- borderColor: style.borderColor,
- color: style.color,
- };
- });
await closeShort.click();
await expect(panel.getByRole('button', { name: '停止平空' })).toBeEnabled();
await expect(closeLong).toBeDisabled();
- await expect(closeLong).toHaveAttribute('data-ladder-preserve-tone', 'true');
- expect(await closeLong.evaluate((button) => {
- const style = getComputedStyle(button);
- return {
- backgroundColor: style.backgroundColor,
- borderColor: style.borderColor,
- color: style.color,
- };
- })).toEqual(closeLongTone);
+ await expectStandardDisabledStyle(closeLong);
expect(errors).toEqual([]);
});
diff --git a/scripts/binance-orderbook-trade.user.js b/scripts/binance-orderbook-trade.user.js
index 6177104..eec7791 100644
--- a/scripts/binance-orderbook-trade.user.js
+++ b/scripts/binance-orderbook-trade.user.js
@@ -3,7 +3,7 @@
// @namespace binance.orderbook.trade
// @icon data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%3E%3Crect%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2214%22%20fill%3D%22%23f0b90b%22%2F%3E%3Ctext%20x%3D%2232%22%20y%3D%2249%22%20text-anchor%3D%22middle%22%20font-family%3D%22Arial%2C%20sans-serif%22%20font-size%3D%2242%22%20font-weight%3D%22800%22%20fill%3D%22%23111827%22%3EJ%3C%2Ftext%3E%3C%2Fsvg%3E
// @icon64 data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%3E%3Crect%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2214%22%20fill%3D%22%23f0b90b%22%2F%3E%3Ctext%20x%3D%2232%22%20y%3D%2249%22%20text-anchor%3D%22middle%22%20font-family%3D%22Arial%2C%20sans-serif%22%20font-size%3D%2242%22%20font-weight%3D%22800%22%20fill%3D%22%23111827%22%3EJ%3C%2Ftext%3E%3C%2Fsvg%3E
-// @version 2.7.131
+// @version 2.7.132
// @author jackhai9
// @description 单击订单簿价格,按当前开仓/平仓 tab 自动填数量并执行下单,内置数量倍率面板
// @match https://www.binance.com/*/futures/*
@@ -1973,7 +1973,7 @@
style.id = styleId;
style.textContent = `
button[${NATIVE_ACTION_DISABLED_ATTR}="true"],
- #${PANEL_ID} button:disabled:not([data-ladder-preserve-tone="true"]) {
+ #${PANEL_ID} button:disabled {
background: ${DISABLED_CONTROL_BG} !important;
color: ${DISABLED_CONTROL_TEXT} !important;
border-color: ${DISABLED_CONTROL_BORDER} !important;
@@ -5648,23 +5648,17 @@
""
].join("");
}
- function ladderActionButton(actionType, label, tone, disabled = false, preserveTone = false) {
+ function ladderActionButton(actionType, label, tone, disabled = false) {
const isBuyTone = tone === "BUY";
const borderColor = isBuyTone ? "var(--color-Buy)" : "var(--color-Sell)";
const background = isBuyTone ? "var(--color-GreenAlpha01)" : "var(--color-RedAlpha01)";
- const disabledAttrs = disabled ? ` disabled aria-disabled="true"${preserveTone ? ' data-ladder-preserve-tone="true"' : ""}` : "";
+ const disabledAttrs = disabled ? ' disabled aria-disabled="true"' : "";
const cursor = disabled ? "not-allowed" : "pointer";
return ``;
}
- function ladderExecutionButton(actionType, label, tone, disabled = false, preserveTone = false) {
+ function ladderExecutionButton(actionType, label, tone, disabled = false) {
if (activeLadderActionType !== actionType) {
- return ladderActionButton(
- actionType,
- label,
- tone,
- disabled,
- Boolean(activeLadderActionType) && preserveTone
- );
+ return ladderActionButton(actionType, label, tone, disabled);
}
return ``;
}
@@ -5691,8 +5685,8 @@
ladderOptionRow(PANEL_COPY.field.interval, PANEL_COPY.tooltip.interval, LADDER_STEP_OPTIONS, getLadderStep(tradeMode, symbol, precision), "step", "")
],
actionButtons: [
- ladderExecutionButton("OPEN_LONG", PANEL_COPY.action.openLong, "BUY", actionDisabled, true),
- ladderExecutionButton("OPEN_SHORT", PANEL_COPY.action.openShort, "SELL", actionDisabled, true)
+ ladderExecutionButton("OPEN_LONG", PANEL_COPY.action.openLong, "BUY", actionDisabled),
+ ladderExecutionButton("OPEN_SHORT", PANEL_COPY.action.openShort, "SELL", actionDisabled)
]
};
}
@@ -5717,15 +5711,13 @@
"CLOSE_LONG",
PANEL_COPY.action.closeLong,
"SELL",
- closeLongDisabled,
- Boolean(closeContext?.knowsLong && closeContext?.hasLong)
+ closeLongDisabled
),
ladderExecutionButton(
"CLOSE_SHORT",
PANEL_COPY.action.closeShort,
"BUY",
- closeShortDisabled,
- Boolean(closeContext?.knowsShort && closeContext?.hasShort)
+ closeShortDisabled
)
]
};
diff --git a/src/binance-orderbook-trade/index.user.js b/src/binance-orderbook-trade/index.user.js
index 1e02dad..381f542 100644
--- a/src/binance-orderbook-trade/index.user.js
+++ b/src/binance-orderbook-trade/index.user.js
@@ -3,7 +3,7 @@
// @namespace binance.orderbook.trade
// @icon data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%3E%3Crect%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2214%22%20fill%3D%22%23f0b90b%22%2F%3E%3Ctext%20x%3D%2232%22%20y%3D%2249%22%20text-anchor%3D%22middle%22%20font-family%3D%22Arial%2C%20sans-serif%22%20font-size%3D%2242%22%20font-weight%3D%22800%22%20fill%3D%22%23111827%22%3EJ%3C%2Ftext%3E%3C%2Fsvg%3E
// @icon64 data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2064%2064%22%3E%3Crect%20width%3D%2264%22%20height%3D%2264%22%20rx%3D%2214%22%20fill%3D%22%23f0b90b%22%2F%3E%3Ctext%20x%3D%2232%22%20y%3D%2249%22%20text-anchor%3D%22middle%22%20font-family%3D%22Arial%2C%20sans-serif%22%20font-size%3D%2242%22%20font-weight%3D%22800%22%20fill%3D%22%23111827%22%3EJ%3C%2Ftext%3E%3C%2Fsvg%3E
-// @version 2.7.131
+// @version 2.7.132
// @author jackhai9
// @description 单击订单簿价格,按当前开仓/平仓 tab 自动填数量并执行下单,内置数量倍率面板
// @match https://www.binance.com/*/futures/*
@@ -393,7 +393,7 @@ import {
style.id = styleId;
style.textContent = `
button[${NATIVE_ACTION_DISABLED_ATTR}="true"],
- #${PANEL_ID} button:disabled:not([data-ladder-preserve-tone="true"]) {
+ #${PANEL_ID} button:disabled {
background: ${DISABLED_CONTROL_BG} !important;
color: ${DISABLED_CONTROL_TEXT} !important;
border-color: ${DISABLED_CONTROL_BORDER} !important;
@@ -4811,26 +4811,18 @@ import {
].join('');
}
- function ladderActionButton(actionType, label, tone, disabled = false, preserveTone = false) {
+ function ladderActionButton(actionType, label, tone, disabled = false) {
const isBuyTone = tone === 'BUY';
const borderColor = isBuyTone ? 'var(--color-Buy)' : 'var(--color-Sell)';
const background = isBuyTone ? 'var(--color-GreenAlpha01)' : 'var(--color-RedAlpha01)';
- const disabledAttrs = disabled
- ? ` disabled aria-disabled="true"${preserveTone ? ' data-ladder-preserve-tone="true"' : ''}`
- : '';
+ const disabledAttrs = disabled ? ' disabled aria-disabled="true"' : '';
const cursor = disabled ? 'not-allowed' : 'pointer';
return ``;
}
- function ladderExecutionButton(actionType, label, tone, disabled = false, preserveTone = false) {
+ function ladderExecutionButton(actionType, label, tone, disabled = false) {
if (activeLadderActionType !== actionType) {
- return ladderActionButton(
- actionType,
- label,
- tone,
- disabled,
- Boolean(activeLadderActionType) && preserveTone,
- );
+ return ladderActionButton(actionType, label, tone, disabled);
}
return ``;
}
@@ -4858,8 +4850,8 @@ import {
ladderOptionRow(PANEL_COPY.field.interval, PANEL_COPY.tooltip.interval, LADDER_STEP_OPTIONS, getLadderStep(tradeMode, symbol, precision), 'step', ''),
],
actionButtons: [
- ladderExecutionButton('OPEN_LONG', PANEL_COPY.action.openLong, 'BUY', actionDisabled, true),
- ladderExecutionButton('OPEN_SHORT', PANEL_COPY.action.openShort, 'SELL', actionDisabled, true),
+ ladderExecutionButton('OPEN_LONG', PANEL_COPY.action.openLong, 'BUY', actionDisabled),
+ ladderExecutionButton('OPEN_SHORT', PANEL_COPY.action.openShort, 'SELL', actionDisabled),
],
};
}
@@ -4886,14 +4878,12 @@ import {
PANEL_COPY.action.closeLong,
'SELL',
closeLongDisabled,
- Boolean(closeContext?.knowsLong && closeContext?.hasLong),
),
ladderExecutionButton(
'CLOSE_SHORT',
PANEL_COPY.action.closeShort,
'BUY',
closeShortDisabled,
- Boolean(closeContext?.knowsShort && closeContext?.hasShort),
),
],
};
diff --git a/test/unit/binance-orderbook-trade/ladder-options.test.js b/test/unit/binance-orderbook-trade/ladder-options.test.js
index cc9b17a..8f6c930 100644
--- a/test/unit/binance-orderbook-trade/ladder-options.test.js
+++ b/test/unit/binance-orderbook-trade/ladder-options.test.js
@@ -246,8 +246,8 @@ test('ladder quantity levels and step options share one stable five-slot grid',
});
test('close ladder buttons match the Binance native long-short order', () => {
- const closeLongButton = "'CLOSE_LONG',\n PANEL_COPY.action.closeLong,\n 'SELL',\n closeLongDisabled,\n Boolean(closeContext?.knowsLong && closeContext?.hasLong)";
- const closeShortButton = "'CLOSE_SHORT',\n PANEL_COPY.action.closeShort,\n 'BUY',\n closeShortDisabled,\n Boolean(closeContext?.knowsShort && closeContext?.hasShort)";
+ const closeLongButton = "'CLOSE_LONG',\n PANEL_COPY.action.closeLong,\n 'SELL',\n closeLongDisabled,\n )";
+ const closeShortButton = "'CLOSE_SHORT',\n PANEL_COPY.action.closeShort,\n 'BUY',\n closeShortDisabled,\n )";
assert.notEqual(source.indexOf(closeLongButton), -1);
assert.notEqual(source.indexOf(closeShortButton), -1);
diff --git a/test/unit/binance-orderbook-trade/source-regressions.test.js b/test/unit/binance-orderbook-trade/source-regressions.test.js
index 6f9e681..e516a86 100644
--- a/test/unit/binance-orderbook-trade/source-regressions.test.js
+++ b/test/unit/binance-orderbook-trade/source-regressions.test.js
@@ -883,11 +883,8 @@ test('cancel current-symbol open orders are single-flight and follow the native
assert.match(panelBody, /noOrdersFeedback: cancelNoOrdersFeedbackActive/);
assert.match(source, /let activeLadderActionType = null/);
assert.match(source, /let activeLadderPanelContext = null/);
- assert.match(source, /data-ladder-preserve-tone="true"/);
- assert.match(source, /button:disabled:not\(\[data-ladder-preserve-tone="true"\]\)/);
- assert.match(source, /Boolean\(activeLadderActionType\) && preserveTone/);
- assert.match(source, /Boolean\(closeContext\?\.knowsLong && closeContext\?\.hasLong\)/);
- assert.match(source, /Boolean\(closeContext\?\.knowsShort && closeContext\?\.hasShort\)/);
+ assert.match(source, /#\$\{PANEL_ID\} button:disabled/);
+ assert.doesNotMatch(source, /data-ladder-preserve-tone|preserveTone/);
assert.match(panelBody, /controlSections\.actionButtons/);
assert.doesNotMatch(panelBody, /grid-column:span 2/);
assert.match(panelBody, /grid-template-columns:repeat\(3,minmax\(0,1fr\)\)/);
@@ -933,7 +930,7 @@ test('cancel current-symbol open orders are single-flight and follow the native
const actionRowsBody = readFunctionBody('getLadderControlSections');
assert.match(actionRowsBody, /actionDisabled = ladderRunning \|\| cancelCurrentSymbolOpenOrdersBlocksLadderActions/);
assert.doesNotMatch(actionRowsBody, /!!cancelCurrentSymbolOpenOrdersTask/);
- assert.match(actionRowsBody, /ladderExecutionButton\('OPEN_LONG',[\s\S]*actionDisabled, true\)/);
+ assert.match(actionRowsBody, /ladderExecutionButton\('OPEN_LONG',[\s\S]*actionDisabled\)/);
assert.match(startBody, /activeLadderActionType = actionType/);
assert.match(startBody, /activeLadderPanelContext = \{[\s\S]*mode: spec\.mode,[\s\S]*symbol: actionSymbol,[\s\S]*precision: readCurrentOrderbookPrecisionValue\(\)/);
assert.match(startBody, /finally\(\(\) => \{[\s\S]*activeLadderActionType = null/);