You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Storybook如何创建支持Knob切换图标的输入组件

实现方案

核心原因是组件图标容器位置硬编码了<Wallet />组件,完全没有读取传入的icon prop做匹配渲染,无论Storybook的knobs控件切换到什么值,这里永远固定渲染钱包图标。另外原代码的输入宽度自适应方法存在变量名笔误,输入时会触发JS报错,以下是完整可运行的修正方案:


1. 改造组件,新增图标映射逻辑

不要在JSX里写多分支判断选图标,维护一个图标名和组件的映射对象是成本最低的方案,后续加新图标也不用改渲染逻辑。修正后的组件代码如下:

import React, { useState, useEffect } from "react";
import "./input-withIcon.css";
import PropTypes from "prop-types";
import Wallet from "../../icons-jsx/Wallet";
import Account from "../../icons-jsx/Account";
// 后续新增图标先在这里导入
import Home from "../../icons-jsx/Home";
import Facebook from "../../icons-jsx/Facebook";

// *映射表的key必须和prop传入值、knobs选项值完全一致*
const ICON_MAP = {
  wallet: Wallet,
  account: Account,
  home: Home,
  facebook: Facebook
};

const InputWithIcon = ({ children, icon }) => {
  const [input, setInput] = useState("");

  useEffect(() => {
    adjustInputSize();
  }, [input]);

  const handleChange = (e) => {
    setInput(e.target.value);
  };

  const adjustInputSize = () => {
    const inputEl = document.getElementById("iconInput");
    // 修复原代码笔误:之前错误使用input.length、Input.style导致报错
    if (inputEl.value.length < 1) {
      inputEl.style.width = "28px";
    } else {
      inputEl.style.width = `${inputEl.value.length + 8}ch`;
    }
  };

  // 从映射表匹配当前要渲染的图标,匹配不到返回null
  const CurrentIcon = ICON_MAP[icon] || null;

  return (
    <div className="input-container1">
      {/* 替换原硬编码的<Wallet />,动态渲染匹配到的图标 */}
      {CurrentIcon && (
        <div className="icon-input__icon">
          <CurrentIcon />
        </div>
      )}
      <div className="input-container-withIcon">
        <input
          type="text"
          required
          className={`iicon-input ${CurrentIcon ? "icon-input__with-icon" : ""}`}
          id="iconInput"
          value={input}
          onChange={handleChange}
        />
        <label className="ibl-icon-label">{children}</label>
      </div>
    </div>
  );
};

InputWithIcon.propTypes = {
  children: PropTypes.node,
  // 新增图标后同步更新这里的枚举值
  icon: PropTypes.oneOf(["wallet", "account", "home", "facebook"]),
};
export default InputWithIcon;

2. 更新Storybook的knobs配置

把新增的图标选项补充到knobs的select配置里,保证选项值和映射表key一致即可:

import IconInput from "../components/fields/input-with-icon/InputWithIcon";
import { withKnobs, text, select } from "@storybook/addon-knobs";
import { withInfo } from "@storybook/addon-info";

export default {
  title: "Input Fields",
  parameters: {
    component: IconInput,
    decorators: [withKnobs, withInfo],
    info: {
      text: "带前置图标的输入框组件,支持切换图标类型、自定义标签和提示文案",
    },
    "in-dsm": {
      id: "",
      componentPath: "../components/fields/input-with-icon/InputWithIcon",
    },
  },
};

const iconOptions = { 
  none: null, 
  wallet: "wallet", 
  account: "account",
  home: "home",
  facebook: "facebook"
};
export const InputIcon = () => (
  <div>
    <IconInput icon={select("icon", iconOptions, iconOptions.wallet)}>
      {text("label", "Label")}
    </IconInput>
    <small className="hint-with-icon">{text("hint text", "Hint text")}</small>
  </div>
);

后续扩展说明

  • 新增图标只需要三步:导入对应图标组件、在ICON_MAP中添加键值对、同步更新PropTypes枚举和story的iconOptions,不需要修改核心渲染逻辑
  • 不要用多层三元表达式、if/else判断渲染图标,图标数量多了之后代码会非常难维护,映射表方案的可读性和扩展性最好
  • 如果需要支持无图标状态,保持icon传null即可,逻辑已经做了兼容

内容的提问来源于stack exchange,提问作者TEbogo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 19:56:50