⌈Vue3&React18⌋动手实践构建组件库之❣️Icon组件❣️

283 阅读1分钟

预览

Icon | Vue3xy (chengbotao.github.io)
Icon | Reactxy (chengbotao.github.io)

介绍

基于 Font Awesome 免费图标二次封装
Font Awesome | free

// 使用 Icon 组件前置条件需引入一下依赖
import { library } from '@fortawesome/fontawesome-svg-core';
// 可以按需引入或全量引入免费图标
import { fas } from '@fortawesome/free-solid-svg-icons';
library.add(fas);

props属性

更多属性参考 Font AwesomeFontAwesomeIconProps

属性名属性类型说明默认值
themeoneOf "primary" | "danger" | "secondary" | "success" | "info" | "warning" | "light" | "dark"设置 Icon 主题primary
classNamestring自定义CSS类名-
iconIconPropicon名字-

呈现

Vue3 实现

<template>
    <FontAwesomeIcon v-bind="$attrs" :icon="icon" :class="classes"></FontAwesomeIcon>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
    name: 'XyIcon',
    inheritAttrs: false
    
})

</script>

<script setup lang="ts">
import { computed } from "vue";
import classNames from "classnames";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon, FontAwesomeIconProps } from "@fortawesome/vue-fontawesome";

type ThemeProps =
    | 'primary'
    | 'secondary'
    | 'success'
    | 'info'
    | 'warning'
    | 'danger'
    | 'light'
    | 'dark';

interface IconProps extends Omit<FontAwesomeIconProps, "icon"> {
    icon: object | Array<string> | string | IconDefinition
    className?: string
    theme?: ThemeProps
}

// Props
const props = withDefaults(defineProps<IconProps>(), {
    theme: "primary"
});

// computed
const classes = computed(() => {
    return classNames("xy-icon", props.className, {
        [`xy-icon-${props.theme}`]: props.theme,
    });
})
</script>

Vue3 Icon 单元测试

import { render } from '@testing-library/vue';
import Icon from './index';

import { library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/free-solid-svg-icons';
library.add(fas);

describe('Icon Component', () => {
  // 应呈现正确的 icon 组件
  test('should render the correct icon', async () => {
    const { getByTestId } = render(Icon, {
      props: {
        icon: 'coffee',
      },
      attrs: {
        'data-testid': 'xyicon',
      },
    });

    const element = getByTestId('xyicon');

    expect(element.className).toMatch(/xy-icon/);
  });
});

React 实现

import React, { FC } from 'react';
import classNames from 'classnames';
import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
import { IconProp } from '@fortawesome/fontawesome-svg-core';

export type ThemeProps =
  | 'primary'
  | 'secondary'
  | 'success'
  | 'info'
  | 'warning'
  | 'danger'
  | 'light'
  | 'dark';

export interface IconProps extends FontAwesomeIconProps {
  /** 设置 Icon 主题 */
  theme?: ThemeProps;
  /** 自定义 css 类名 */
  className?: string;
  /** icon 名字 */
  icon: IconProp;
}

/**
 * Icon 组件
 *
 *
 */
export const Icon: FC<IconProps> = (props) => {
  const { className, theme, ...restProps } = props;
  const classes = classNames('xy-icon', className, {
    [`xy-icon-${theme}`]: theme,
  });

  return <FontAwesomeIcon className={classes} {...restProps}></FontAwesomeIcon>;
};

export default Icon;

React Icon 单元测试

import React from 'react';
import { render } from '@testing-library/react';
import Icon, { IconProps } from './icon';

import { library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/free-solid-svg-icons';
library.add(fas);

describe('Icon Component', () => {
  test('should render the correct icon', () => {
    const defaultProps: IconProps = {
      icon: 'coffee',
    };
    const wrapper = render(<Icon data-testid="xyicon" {...defaultProps}></Icon>);
    const element = wrapper.getByTestId('xyicon');
    expect(element).toBeInTheDocument();
    expect(element).toHaveClass('xy-icon');
  });
});

后记

Font Awesome
Set Up with Vue | Font Awesome Docs
Set Up with React | Font Awesome Docs
Classic Solid Style Icons | Font Awesome
个人博客 | Botaoxy (chengbotao.github.io)

感谢阅读,敬请斧正!