Skip to content

fix(Select): improved label filtering#3891

Open
RylanBot wants to merge 18 commits into
developfrom
rylan/feat/select/filterable
Open

fix(Select): improved label filtering#3891
RylanBot wants to merge 18 commits into
developfrom
rylan/feat/select/filterable

Conversation

@RylanBot

@RylanBot RylanBot commented Oct 13, 2025

Copy link
Copy Markdown
Collaborator

🤔 这个 PR 的性质是?

  • 日常 bug 修复
  • 新特性提交
  • 文档改进
  • 演示代码改进
  • 组件样式/交互改进
  • CI/CD 改进
  • 重构
  • 代码风格优化
  • 测试用例
  • 分支合并
  • 其他

🔗 相关 Issue

TreeSelect

Demo 代码 1
import React, { useEffect, useState } from 'react';
import { Select, Space, Switch, Tag } from 'tdesign-react';

export default function TreeSelectDemo() {
  const [autoWidth, setAutoWidth] = useState(false);
  const [data, setData] = useState([]);
  const [selectValue, setSelectValue] = useState('');

  const genFieldOption = ({ detail }) => {
    const value = `${detail.field_name}${detail.field_version ? ':' : ''}${detail.field_version ?? ''}`;
    const fieldNameNoVersion = `${detail.field_name}`;
    const showValue = `${fieldNameNoVersion}(${detail.field_name_ch})`;

    return {
      label: (
        <>
          <Tag size="small">{detail.meta_type}</Tag>
          {fieldNameNoVersion}
          <span className="t-input__comment">{detail.field_name_ch}</span>
        </>
      ),
      value,
      text: showValue,
      detail,
    };
  };

  const initList = async () => {
    const list = [
      {
        field_name: 'app_detail_service_getuserdata_request',
        field_name_ch: 'GetUserData Request',
        field_version: '1.0',
        meta_type: 'contract-sub',
      },
      {
        field_name: 'app_detail_service_getuserdata_reply',
        field_name_ch: 'GetUserData Reply',
        field_version: '1.0',
        meta_type: 'contract-sub',
      },
      {
        field_name: 'app_detail_service_getuserdata',
        field_name_ch: 'GetUserData',
        field_version: '1.0',
        meta_type: 'contract',
      },
      { field_name: 't_user_data', field_name_ch: '用户信息', field_version: '1.0', meta_type: 'storage' },
      {
        field_name: 't_midas_suborder_state',
        field_name_ch: '支付订单信息',
        field_version: '1.0',
        meta_type: 'storage',
      },
      { field_name: 'pay_channel', field_name_ch: '支付渠道', field_version: '1.0', meta_type: 'basic' },
      { field_name: 'pay_channel_sub_id', field_name_ch: '支付子渠道', field_version: '1.0', meta_type: 'basic' },
      { field_name: 'channel_order_id', field_name_ch: '渠道单号', field_version: '1.0', meta_type: 'basic' },
    ];
    setData(list.map((item) => genFieldOption({ detail: item })));
  };

  useEffect(() => {
    initList();
  }, []);

  const renderValueDisplay = (children) => {
    if (!children?.[1]) return '';
    return (
      <>
        {children[0]}
        {children[1]}
        <span className="t-input__comment">{children[2]?.props?.children}</span>
      </>
    );
  };

  const selectedOption = (data as any[]).find((item) => item.value === selectValue);
  const selectValueDisplay = selectedOption ? renderValueDisplay(selectedOption.label?.props?.children) : undefined;

  return (
    <Space direction="vertical">
      <div>
        <Switch<boolean> value={autoWidth} onChange={setAutoWidth} />
        Auto Width
      </div>

      <Select
        options={data}
        size="medium"
        value={selectValue || ''}
        onChange={(value) => {
          console.log(value);
          setSelectValue(value as string);
        }}
        clearable
        filterable
        valueDisplay={selectValueDisplay}
        autoWidth={autoWidth}
        {...(!autoWidth && { style: { width: 300 }, inputProps: { style: { width: 300 } } })}
      />
    </Space>
  );
}

💡 需求背景和解决方案

Cascader

Select:(历史相关 PR:#973 (comment))

Demo 代码 2
import React, { useState } from "react";
import { Select, Tooltip } from "tdesign-react";

export default function SingleSelect() {
  const [value, setValue] = useState("");
  const onChange = (value: string) => {
    setValue(value);
  };

  return (
    <Select
      value={value}
      onChange={onChange}
      style={{ width: "40%" }}
      clearable
      options={[
        { label: "架构云", value: "1", title: "架构云选项" },
        { label: "大数据", value: "2" },
        { label: "区块链", value: "3" },
        { label: "物联网", value: "4", disabled: true },
        {
          label: "人工智能",
          value: "5",
          content: (
            <Tooltip content="人工智能">
              <span>人工智能(新)</span>
            </Tooltip>
          ),
          title: null,
        },
        {
          label: "云原生",
          value: "6",
          content: (
            <div style={{ display: "flex", flexDirection: "column", lineHeight: 1.4, padding: "4px 0" }}>
              <span>云原生</span>
              <span style={{ fontSize: 12, color: "var(--td-text-color-placeholder)" }}>
                Cloud Native · 容器化、微服务、DevOps
              </span>
            </div>
          ),
          title: null,
        },
        {
          label: (
            <div style={{ display: "flex", flexDirection: "column", lineHeight: 1.4, padding: "4px 0" }}>
              <span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
                <span
                  style={{
                    display: "inline-block",
                    width: 6,
                    height: 6,
                    borderRadius: "50%",
                    background: "var(--td-brand-color)",
                  }}
                />
                <span>边缘计算</span>
                <span
                  style={{
                    fontSize: 12,
                    padding: "0 4px",
                    borderRadius: 2,
                    color: "var(--td-brand-color)",
                    background: "var(--td-brand-color-light)",
                  }}
                >
                  HOT
                </span>
              </span>
              <span style={{ fontSize: 12, color: "var(--td-text-color-placeholder)" }}>
                Edge Computing · 低延迟、就近处理
              </span>
            </div>
          ),
          value: "7",
          title: null,
        },
      ]}
    />
  );
}

(截图为当前效果...Input 框的撑开高度交给用户自己设置 inputClass 更加自由,下拉框的选项高度,通过组件库主动给高度设 min-height 比较合理)

📝 更新日志

  • fix(Select): 修复 label 为非 string 类型时,开启 filterable 搜索后报错的问题。

  • fix(Select): 修复使用 label / content / children 自定义元素时,下拉框选项的高度没有撑开的问题。 (common#2537)

  • fix(SelectInput): 修复单选且 valueDisplay 为自定义元素时,开启 filterable 无法显示输入内容的问题。该特性影响了组件的 DOM,之前依赖相关结构的业务注意此变更 ⚠️

  • fix(SelectInput): 修复 inputProps.inputClass 无法生效的问题。先前 inputProps.className 被错误地应用到了 t-class 同级位置,导致正确的 inputProps.inputClass 配置无效,之前使用相关属性的业务注意此变更 ⚠️

  • feat(SelectInput): 支持 focus 时的内容为 valueDisplay 的对应值。

  • 本条 PR 不需要纳入 Changelog

☑️ 请求合并前的自查清单

⚠️ 请自检并全部勾选全部选项⚠️

  • 文档已补充或无须补充
  • 代码演示已提供或无须提供
  • TypeScript 定义已补充或无须补充
  • Changelog 已提供或无须提供

@pkg-pr-new

pkg-pr-new Bot commented Oct 13, 2025

Copy link
Copy Markdown
  • tdesign-react-demo

    npm i https://pkg.pr.new/Tencent/tdesign-react@3891
    
    npm i https://pkg.pr.new/Tencent/tdesign-react/@tdesign-react/chat@3891
    

commit: f362de1

@github-actions

github-actions Bot commented Oct 13, 2025

Copy link
Copy Markdown
Contributor

TDesign Component Site Preview Open

Component Preview
tdesign-react 完成
@tdesign-react/chat 完成

@RylanBot RylanBot added the WIP work in porgess label Oct 14, 2025
@RylanBot RylanBot force-pushed the rylan/feat/select/filterable branch from fc47b2a to 8ec8e7d Compare October 15, 2025 07:08
@RylanBot RylanBot removed the WIP work in porgess label Oct 15, 2025
@RylanBot RylanBot requested a review from Copilot October 15, 2025 10:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR improves label filtering functionality in the Select component and related components (SelectInput, Cascader, TreeSelect), fixing several issues with custom node rendering and input behavior.

Key Changes:

  • Fixed errors when filtering Select options with custom label nodes (ReactElements)
  • Fixed rendering issues for custom content in Select dropdown options
  • Improved single-selection input value display for SelectInput when labels are custom nodes
  • Fixed inputProps.inputClass not being applied correctly

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/components/select/base/Select.tsx Added extractTextFromTNode utility to enable filtering custom label nodes; fixed type annotations
packages/components/select/base/Option.tsx Added auto-height styling for custom element content; removed unreachable return statement
packages/components/select/_example/custom-options.tsx Refactored demo to show both slot-based and content attribute approaches for custom options
packages/components/select-input/useSingle.tsx Refactored single-select value display logic to properly render custom label nodes with absolute positioning
packages/components/_util/parseTNode.ts Added extractTextFromTNode utility function to recursively extract text from TNode structures
packages/components/tree-select/TreeSelect.tsx Simplified input value logic by removing redundant display handling code
test/snap/__snapshots__/ssr.test.jsx.snap Updated snapshots to reflect DOM structure changes
test/snap/__snapshots__/csr.test.jsx.snap Updated snapshots to reflect DOM structure changes

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread packages/components/select-input/useSingle.tsx Outdated
Comment thread packages/components/_util/parseTNode.ts
Comment thread packages/components/tree-select/TreeSelect.tsx
@RylanBot RylanBot force-pushed the rylan/feat/select/filterable branch from 9d1f8c9 to ba42885 Compare October 16, 2025 07:36
@RylanBot RylanBot added WIP work in porgess and removed WIP work in porgess labels Nov 27, 2025
@RylanBot RylanBot removed the WIP work in porgess label Nov 27, 2025
ref={setRefCurrent}
style={style}
style={{
...(isCustomElement ? { height: 'auto' } : {}),

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

使用 style 覆盖 css 里固定的 height

return normalizedValue.length ? displayNode : '';
}, [valueDisplay, multiple, normalizedValue]);

const internalInputValueDisplay: SelectInputProps['valueDisplay'] = useMemo(() => {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这些逻辑全部交给 SelectInputuseSingle 内部处理

const labelNode = showLabelNode ? (
<div
style={{
position: 'absolute',

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这段新增的 div 以及样式...看是否要往 common 仓新增一个 class 统一管理 (?)

Vue 端在点击 Input 后,valueDisplay 会恢复为 value / label

React 端这次调整后采取保留原有的效果

value={singleValueDisplay ? ' ' : displayedValue}
label={
(props.label || singleValueDisplay) && (
suffix={

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

原本的 displayValue 放在左侧 label
单选时光标位置在内容后面不太合理
现在移到 suffix,然后通过 style 绝对定位显示在左侧

或者为了减少 DOM 结构的变更
在不开启 filterable / allowInput 时,依旧使用 label 渲染 (?)
临时兼容的话我可以在这里补充对应逻辑...但未来为了方便管理,我还是偏向迟早有一个版本彻底使用 suffix

@RylanBot RylanBot added the breaking the changes involve a large amount of code label Jan 19, 2026
@RylanBot RylanBot force-pushed the rylan/feat/select/filterable branch from fc381b5 to d6e3e2a Compare May 20, 2026 06:57
@RylanBot RylanBot force-pushed the rylan/feat/select/filterable branch from d6e3e2a to 7179cb0 Compare May 20, 2026 10:56
@RylanBot

Copy link
Copy Markdown
Collaborator Author

/update-common

@RSS1102 RSS1102 force-pushed the rylan/feat/select/filterable branch from b797ecb to a3a6c55 Compare June 10, 2026 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking the changes involve a large amount of code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants