BoxSelectionComponent.tsx
import React, { useState } from 'react';
import { Modal, Button, Table, message } from 'antd';

const BoxSelectionComponent: React.FC = () => {
  const [modalVisible, setModalVisible] = useState(false);
  const [selectedBoxes, setSelectedBoxes] = useState<string[]>([]);

  const boxes = ['A', 'B', 'C', 'D', 'E'];

  const showModal = () => {
    setModalVisible(true);
  };

  const handleBoxSelect = (box: string) => {
    const isSelected = selectedBoxes.includes(box);
    if (isSelected) {
      const updatedSelection = selectedBoxes.filter((selectedBox) => selectedBox !== box);
      setSelectedBoxes(updatedSelection);
    } else {
      if (selectedBoxes.length < 3) {
        setSelectedBoxes([...selectedBoxes, box]);
      } else {
        message.warning('最多可以选中三个盒子');
      }
    }
  };

  const handleDelete = (box: string) => {
    const updatedSelection = selectedBoxes.filter((selectedBox) => selectedBox !== box);
    setSelectedBoxes(updatedSelection);

    if (updatedSelection.length === 0) {
      message.warning('至少选择一个盒子');
    }
  };

  const handleOk = () => {
    if (selectedBoxes.length === 0) {
      message.warning('至少选择一个盒子');
      return;
    }

    // 处理选中的盒子,可以在这里进行进一步操作,比如提交数据
    console.log('选中的盒子:', selectedBoxes);

    setModalVisible(false);
  };

  const handleCancel = () => {
    setModalVisible(false);
  };

  const columns = [
    {
      title: '序号',
      dataIndex: 'index',
      key: 'index',
      render: (_: any, record: { index: number }) => record.index + 1,
    },
    {
      title: '名称',
      dataIndex: 'name',
      key: 'name',
    },
    {
      title: '操作',
      key: 'action',
      render: (_: any, record: { index: number; name: string }) => (
        <>
          <Button type="link" onClick={() => handleView(record)}>
            查看
          </Button>
          <Button type="link" onClick={() => handleDelete(record.name)}>
            删除
          </Button>
        </>
      ),
    },
  ];

  const data = selectedBoxes.map((box, index) => ({
    key: index,
    index,
    name: box,
  }));

  const handleView = (record: { index: number; name: string }) => {
    // 处理查看操作,可以根据需要展示盒子的详细信息等
    console.log('查看盒子:', record);
  };

  return (
    <>
      <Button type="primary" onClick={showModal}>
        打开Modal
      </Button>
      <Modal
        title="选择盒子"
        visible={modalVisible}
        onOk={handleOk}
        onCancel={handleCancel}
      >
        <div>
          {boxes.map((box) => (
            <div
              key={box}
              style={{
                border: '1px solid #ccc',
                padding: '10px',
                marginBottom: '10px',
                backgroundColor: selectedBoxes.includes(box) ? '#e6f7ff' : 'white',
                cursor: 'pointer',
              }}
              onClick={() => handleBoxSelect(box)}
            >
              {box}
            </div>
          ))}
        </div>
      </Modal>
      <Table columns={columns} dataSource={data} pagination={false} />
    </>
  );
};

export default BoxSelectionComponent;
 单元测试
import React from 'react';
import { render, fireEvent, waitFor, screen } from '@testing-library/react';
import BoxSelectionComponent from './BoxSelectionComponent';

describe('BoxSelectionComponent', () => {
  // 渲染资源选择器
  it('renders component correctly', () => {
    render(<BoxSelectionComponent />);
    expect(screen.getByText('打开Modal')).toBeInTheDocument();
  });

  // 点击之后可选择课程
  it('opens modal when button is clicked', async () => {
    render(<BoxSelectionComponent />);
    fireEvent.click(screen.getByText('打开Modal'));
    await waitFor(() => {
      expect(screen.getByText('选择盒子')).toBeInTheDocument();
    });
  });

  // 选中课程的盒子应该是什么样以及是否被选中和被取消
  it('selects and deselects boxes correctly', async () => {
    render(<BoxSelectionComponent />);
    fireEvent.click(screen.getByText('打开Modal'));

    const boxA = screen.getByText('A');
    const boxB = screen.getByText('B');
    const boxC = screen.getByText('C');

    fireEvent.click(boxA);
    fireEvent.click(boxB);
    expect(boxA).toHaveStyle('background-color: #e6f7ff'); // 断言第1个已经被选中
    expect(boxA).toHaveStyle('background-color: #e6f7ff'); // 断言第2个已经被选中

    fireEvent.click(boxA);
    expect(boxA).toHaveStyle('background-color: white'); // 再次点击断言第1个已经被删除
    expect(boxB).toHaveStyle('background-color: white'); // 再次点击断言第1个已经被删除
    expect(boxC).toHaveStyle('background-color: white'); // 再次点击断言第1个已经被删除
  });

  // 验证是否可以正常删除
  it('deletes selected boxes correctly', async () => {
    render(<BoxSelectionComponent />);
    fireEvent.click(screen.getByText('打开Modal'));

    const boxA = screen.getByText('A');
    const boxB = screen.getByText('B');
    fireEvent.click(boxA);
    fireEvent.click(boxB);

    const deleteButton = await screen.findAllByText('删除');
    fireEvent.click(deleteButton[0]); // 第一个选中的假设是第一个删除的

    await waitFor(() => {
      expect(screen.queryByText('A')).not.toBeInTheDocument();
    });

    // 触发校验
    expect(screen.getByText('至少选择一个盒子')).toBeInTheDocument();
  });

  // 触发选中最多三个
  it('warns when trying to select more than three boxes', () => {
    render(<BoxSelectionComponent />);
    fireEvent.click(screen.getByText('打开Modal'));

    const boxA = screen.getByText('A');
    const boxB = screen.getByText('B');
    const boxC = screen.getByText('C');
    const boxD = screen.getByText('D');
    fireEvent.click(boxA);
    fireEvent.click(boxB);
    fireEvent.click(boxC);
    fireEvent.click(boxD);

    expect(screen.getByText('最多可以选中三个盒子')).toBeInTheDocument();
  });

})

 

import React, { useState } from 'react';
import { Modal, Button, Table, message } from 'antd';

const BoxSelectionComponent: React.FC = () => {
  const [modalVisible, setModalVisible] = useState(false);
  const [selectedBoxes, setSelectedBoxes] = useState<string[]>([]);

  const boxes = ['A', 'B', 'C', 'D', 'E'];

  const showModal = () => {
    setModalVisible(true);
  };

  const handleBoxSelect = (box: string) => {
    const isSelected = selectedBoxes.includes(box);
    if (isSelected) {
      const updatedSelection = selectedBoxes.filter((selectedBox) => selectedBox !== box);
      setSelectedBoxes(updatedSelection);
    } else {
      if (selectedBoxes.length < 3) {
        setSelectedBoxes([...selectedBoxes, box]);
      } else {
        message.warning('最多可以选中三个盒子');
      }
    }
  };

  const handleDelete = (box: string) => {
    const updatedSelection = selectedBoxes.filter((selectedBox) => selectedBox !== box);
    setSelectedBoxes(updatedSelection);

    if (updatedSelection.length === 0) {
      message.warning('至少选择一个盒子');
    }
  };

  const handleOk = () => {
    if (selectedBoxes.length === 0) {
      message.warning('至少选择一个盒子');
      return;
    }

    // 处理选中的盒子,可以在这里进行进一步操作,比如提交数据
    console.log('选中的盒子:', selectedBoxes);

    setModalVisible(false);
  };

  const handleCancel = () => {
    setModalVisible(false);
  };

  const columns = [
    {
      title: '序号',
      dataIndex: 'index',
      key: 'index',
      render: (_: any, record: { index: number }) => record.index + 1,
    },
    {
      title: '名称',
      dataIndex: 'name',
      key: 'name',
    },
    {
      title: '操作',
      key: 'action',
      render: (_: any, record: { index: number; name: string }) => (
        <>
          <Button type="link" onClick={() => handleView(record)}>
            查看
          </Button>
          <Button type="link" onClick={() => handleDelete(record.name)}>
            删除
          </Button>
        </>
      ),
    },
  ];

  const data = selectedBoxes.map((box, index) => ({
    key: index,
    index,
    name: box,
  }));

  const handleView = (record: { index: number; name: string }) => {
    // 处理查看操作,可以根据需要展示盒子的详细信息等
    console.log('查看盒子:', record);
  };

  return (
    <>
      <Button type="primary" onClick={showModal}>
        打开Modal
      </Button>
      <Modal
        title="选择盒子"
        visible={modalVisible}
        onOk={handleOk}
        onCancel={handleCancel}
      >
        <div>
          {boxes.map((box) => (
            <div
              key={box}
              style={{
                border: '1px solid #ccc',
                padding: '10px',
                marginBottom: '10px',
                backgroundColor: selectedBoxes.includes(box) ? '#e6f7ff' : 'white',
                cursor: 'pointer',
              }}
              onClick={() => handleBoxSelect(box)}
            >
              {box}
            </div>
          ))}
        </div>
      </Modal>
      <Table columns={columns} dataSource={data}
        pagination={false}
      />
    </>
  );
};

export default BoxSelectionComponent;