From d5c0e927c6d62c7b00929af8c0d39cc384e96135 Mon Sep 17 00:00:00 2001 From: David Park <163079241+dparkmit24@users.noreply.github.com> Date: Mon, 27 Jul 2026 02:03:17 +0000 Subject: [PATCH] fix(web): stop chunk length/overlap inputs collapsing to an unusable width in narrow containers (#39600) Co-authored-by: Claude Opus 4.8 (1M context) --- .../general-chunking-options.spec.tsx | 26 ++++++++++++++ .../components/__tests__/inputs.spec.tsx | 35 +++++++++++++++++++ .../components/general-chunking-options.tsx | 6 ++-- .../create/step-two/components/inputs.tsx | 13 +++++-- 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/web/app/components/datasets/create/step-two/components/__tests__/general-chunking-options.spec.tsx b/web/app/components/datasets/create/step-two/components/__tests__/general-chunking-options.spec.tsx index 916c848d7d8..1d5ca3de6e4 100644 --- a/web/app/components/datasets/create/step-two/components/__tests__/general-chunking-options.spec.tsx +++ b/web/app/components/datasets/create/step-two/components/__tests__/general-chunking-options.spec.tsx @@ -197,4 +197,30 @@ describe('GeneralChunkingOptions', () => { expect(onSummaryIndexSettingChange).toHaveBeenCalledWith({ enable: true }) }) }) + + // Regression: dify issue 39592 — the delimiter/max-length/overlap row must + // stack (one field per row) in a narrow card and sit three-across when wide, + // driven by a container query rather than the viewport. Fails before this + // change (row was flex-wrap; no @container ancestor). + describe('#39592 narrow-container regression', () => { + it('stacks by default and becomes a row across the 552px container query', () => { + render() + const delimiterLabel = screen.getByText(`${ns}.stepTwo.separator`) + const row = delimiterLabel.closest('.gap-3') + expect(row).not.toBeNull() + // stacked by default (below threshold) + expect(row!.className).toContain('flex-col') + // three-across at/above the container threshold + expect(row!.className).toContain('@min-[552px]/chunkfields:flex-row') + // the previous flex-wrap approach is gone + expect(row!.className).not.toContain('flex-wrap') + }) + + it('marks an ancestor as the query container', () => { + render() + const delimiterLabel = screen.getByText(`${ns}.stepTwo.separator`) + const container = delimiterLabel.closest('[class*="@container/chunkfields"]') + expect(container).not.toBeNull() + }) + }) }) diff --git a/web/app/components/datasets/create/step-two/components/__tests__/inputs.spec.tsx b/web/app/components/datasets/create/step-two/components/__tests__/inputs.spec.tsx index 0e6cc100763..c74a7244b71 100644 --- a/web/app/components/datasets/create/step-two/components/__tests__/inputs.spec.tsx +++ b/web/app/components/datasets/create/step-two/components/__tests__/inputs.spec.tsx @@ -136,3 +136,38 @@ describe('OverlapInput', () => { expect(onChange).toHaveBeenLastCalledWith(100) }) }) + +// Regression: dify issue 39592 — in a narrow card the number inputs collapsed +// to a 32px, unusable sliver. The fix reflows on the container (a +// @container/chunkfields ancestor, see general-chunking-options): below 552px +// each field stacks and is capped at max-w-[288px] so the input reads as a form +// field; at/above 552px it restores flex-1 (three across, pixel-identical to +// stock). The input keeps a min-width floor as the belt against re-collapse. +// jsdom has no layout engine, so we cannot assert pixel widths — we assert the +// structural classes. These fail before this change and pass after. +describe('#39592 narrow-container regression (structural)', () => { + it('gives the MaxLength input a min-width floor so it can never collapse to a sliver', () => { + render() + const input = screen.getByRole('textbox') + expect(input.className).toContain('min-w-[64px]') + }) + + it('gives the OverlapInput input a min-width floor too', () => { + render() + const input = screen.getByRole('textbox') + expect(input.className).toContain('min-w-[64px]') + }) + + it('caps each field width when stacked and restores flex-1 across the 552px container query', () => { + render() + const field = screen.getByRole('textbox').closest('.space-y-2') + expect(field).not.toBeNull() + // stacked (default / below threshold): constrained width, no stretch + expect(field!.className).toContain('max-w-[288px]') + // three-across (at/above threshold): restore flex-1 and drop the cap + expect(field!.className).toContain('@min-[552px]/chunkfields:flex-1') + expect(field!.className).toContain('@min-[552px]/chunkfields:max-w-none') + // the previous flex-wrap approach is gone + expect(field!.className).not.toContain('basis-[176px]') + }) +}) diff --git a/web/app/components/datasets/create/step-two/components/general-chunking-options.tsx b/web/app/components/datasets/create/step-two/components/general-chunking-options.tsx index 564a7df3cfe..7240690791d 100644 --- a/web/app/components/datasets/create/step-two/components/general-chunking-options.tsx +++ b/web/app/components/datasets/create/step-two/components/general-chunking-options.tsx @@ -127,8 +127,10 @@ export const GeneralChunkingOptions: FC = ({ } noHighlight={isInUpload && isNotUploadInEmptyDataset} > -
-
+
+ {/* Container query, not a viewport breakpoint: three across at/above a + 552px container, stacked one-per-row below (see inputs.tsx FormField). */} +
onSegmentIdentifierChange(e.target.value)} diff --git a/web/app/components/datasets/create/step-two/components/inputs.tsx b/web/app/components/datasets/create/step-two/components/inputs.tsx index afb2788a26e..df3fc8ab8c6 100644 --- a/web/app/components/datasets/create/step-two/components/inputs.tsx +++ b/web/app/components/datasets/create/step-two/components/inputs.tsx @@ -5,6 +5,7 @@ import type { } from '@langgenius/dify-ui/number-field' import type { FC, PropsWithChildren, ReactNode } from 'react' import type { InputProps } from '@/app/components/base/input' +import { cn } from '@langgenius/dify-ui/cn' import { NumberField, NumberFieldControls, @@ -30,7 +31,12 @@ const TextLabel: FC = (props) => { const FormField: FC> = (props) => { return ( -
+ // Reflow on the container (a @container/chunkfields ancestor), not the + // viewport. Below 552px the fields stack one per row, each capped at + // max-w-[288px] so the input reads as a form field, not a full-bleed bar. + // At/above 552px this restores flex-1 with no cap, so three columns resolve + // to (container - gaps)/3 — pixel-identical to the stock flex-1 layout. +
{props.label} {props.children}
@@ -142,7 +148,10 @@ function CompoundNumberInput({ {...inputProps} aria-label={label} size={size} - className={className} + // min-w-[64px] overrides the component's default min-w-0 so the input + // can never collapse to an unusable sliver, even in an unforeseen + // container; belt to the row's flex-wrap braces. + className={cn('min-w-[64px]', className)} onBlur={onBlur} /> {Boolean(unit) && {unit}}