fix(ui): remove duplicate config section headers · openclaw/openclaw@95b7a85
d1rshan
·
2026-04-25
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -438,20 +438,25 @@ export function renderConfigForm(props: ConfigFormProps) {
|
438 | 438 | sectionKey: string; |
439 | 439 | label: string; |
440 | 440 | description: string; |
| 441 | +showHeader: boolean; |
441 | 442 | node: JsonSchema; |
442 | 443 | nodeValue: unknown; |
443 | 444 | path: Array<string | number>; |
444 | 445 | }) => html` |
445 | 446 | <section class="config-section-card" id=${params.id}> |
446 | | - <div class="config-section-card__header"> |
447 | | - <span class="config-section-card__icon">${getSectionIcon(params.sectionKey)}</span> |
448 | | - <div class="config-section-card__titles"> |
449 | | - <h3 class="config-section-card__title">${params.label}</h3> |
450 | | - ${params.description |
451 | | - ? html`<p class="config-section-card__desc">${params.description}</p>` |
452 | | - : nothing} |
453 | | - </div> |
454 | | - </div> |
| 447 | + ${params.showHeader |
| 448 | + ? html` |
| 449 | + <div class="config-section-card__header"> |
| 450 | + <span class="config-section-card__icon">${getSectionIcon(params.sectionKey)}</span> |
| 451 | + <div class="config-section-card__titles"> |
| 452 | + <h3 class="config-section-card__title">${params.label}</h3> |
| 453 | + ${params.description |
| 454 | + ? html`<p class="config-section-card__desc">${params.description}</p>` |
| 455 | + : nothing} |
| 456 | + </div> |
| 457 | + </div> |
| 458 | + ` |
| 459 | + : nothing} |
455 | 460 | <div class="config-section-card__content"> |
456 | 461 | ${renderNode({ |
457 | 462 | schema: params.node, |
@@ -490,6 +495,7 @@ export function renderConfigForm(props: ConfigFormProps) {
|
490 | 495 | sectionKey, |
491 | 496 | label, |
492 | 497 | description, |
| 498 | + showHeader: false, |
493 | 499 | node, |
494 | 500 | nodeValue: scopedValue, |
495 | 501 | path: [sectionKey, subsectionKey], |
@@ -506,6 +512,7 @@ export function renderConfigForm(props: ConfigFormProps) {
|
506 | 512 | sectionKey: key, |
507 | 513 | label: meta.label, |
508 | 514 | description: meta.description, |
| 515 | + showHeader: activeSection == null, |
509 | 516 | node, |
510 | 517 | nodeValue: value[key], |
511 | 518 | path: [key], |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -349,7 +349,80 @@ describe("config view", () => {
|
349 | 349 | (input as HTMLInputElement).value = "gateway"; |
350 | 350 | input.dispatchEvent(new Event("input", { bubbles: true })); |
351 | 351 | expect(onSearchChange).toHaveBeenCalledWith("gateway"); |
| 352 | +}); |
| 353 | + |
| 354 | +it("shows section hero and hides nested card header in single-section form view", () => { |
| 355 | +const { container } = renderConfigView({ |
| 356 | +activeSection: "auth", |
| 357 | +schema: { |
| 358 | +type: "object", |
| 359 | +properties: { |
| 360 | +auth: { |
| 361 | +type: "object", |
| 362 | +properties: { |
| 363 | +authPermanentBackoffMinutes: { |
| 364 | +type: "number", |
| 365 | +}, |
| 366 | +}, |
| 367 | +}, |
| 368 | +}, |
| 369 | +}, |
| 370 | +formValue: { |
| 371 | +auth: { |
| 372 | +authPermanentBackoffMinutes: 10, |
| 373 | +}, |
| 374 | +}, |
| 375 | +originalValue: { |
| 376 | +auth: { |
| 377 | +authPermanentBackoffMinutes: 10, |
| 378 | +}, |
| 379 | +}, |
| 380 | +}); |
352 | 381 | |
| 382 | +const heroTitle = container.querySelector(".config-section-hero__title"); |
| 383 | +expect(heroTitle?.textContent?.trim()).toBe("Authentication"); |
| 384 | +expect(container.querySelector(".config-section-card__header")).toBeNull(); |
| 385 | +}); |
| 386 | + |
| 387 | +it("keeps card headers in multi-section root view", () => { |
| 388 | +const { container } = renderConfigView({ |
| 389 | +schema: { |
| 390 | +type: "object", |
| 391 | +properties: { |
| 392 | +auth: { |
| 393 | +type: "object", |
| 394 | +properties: {}, |
| 395 | +}, |
| 396 | +gateway: { |
| 397 | +type: "object", |
| 398 | +properties: {}, |
| 399 | +}, |
| 400 | +}, |
| 401 | +}, |
| 402 | +formValue: { |
| 403 | +auth: {}, |
| 404 | +gateway: {}, |
| 405 | +}, |
| 406 | +originalValue: { |
| 407 | +auth: {}, |
| 408 | +gateway: {}, |
| 409 | +}, |
| 410 | +}); |
| 411 | + |
| 412 | +expect(container.querySelectorAll(".config-section-card__header").length).toBeGreaterThan(0); |
| 413 | +}); |
| 414 | + |
| 415 | +it("clears the active search query", () => { |
| 416 | +const container = document.createElement("div"); |
| 417 | +const onSearchChange = vi.fn(); |
| 418 | +render( |
| 419 | +renderConfig({ |
| 420 | + ...baseProps(), |
| 421 | +searchQuery: "gateway", |
| 422 | + onSearchChange, |
| 423 | +}), |
| 424 | +container, |
| 425 | +); |
353 | 426 | const clearButton = container.querySelector<HTMLButtonElement>(".config-search__clear"); |
354 | 427 | expect(clearButton).toBeTruthy(); |
355 | 428 | clearButton?.click(); |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。