简体   繁体   English

Jest / RTL 失败——MUI V5 + Emotion 主题问题

[英]Jest / RTL Failure -- MUI V5 + Emotion Theme Issue

I am currently using MUI V5 and Emotion for the styling.我目前正在使用 MUI V5 和 Emotion 进行造型。 The code below works without issue in Storybook, so I'm a bit confused as to why the theme isn't getting injected into the Emotion styling.下面的代码在 Storybook 中没有问题,所以我有点困惑为什么主题没有被注入到 Emotion 样式中。 I would think that the ThemeProvider would handle the injection, but it doesn't seem to be working.我认为 ThemeProvider 会处理注入,但它似乎不起作用。 Any thoughts?有什么想法吗? TIA! TIA!

Badge.tsx

import React, { ReactElement } from 'react';

import * as S from './styles';

export type TBadgeEmphasis = 'subtle' | 'high';
export type TBadgeType = 'default' | 'success' | 'warning' | 'error';

export interface BadgeProps {
  /**
   * Text for the badge.
   */
  children: ReactElement | string;

  /**
   * Option here between `subtle` and `high` depending on the desired intensity.
   */
  emphasis: TBadgeEmphasis;

  /**
   * Option here between `default`, `success`, `warning` and `error`.
   */
  type: string;
}

export const Badge = (props: BadgeProps): ReactElement => {
  return (
    <S.StyledBadge
      data-testid={`${props.type}-${props.emphasis}-badge`}
      emphasis={props.emphasis}
      label={props.children}
      type={props.type}
    />
  );
};

Badge.styles.tsx

import { css } from '@emotion/react';
import { styled } from '@mui/material/styles';
import { Chip as MuiChip, ChipProps as MuiChipProps } from '@mui/material';

interface BadgeStyleProps extends MuiChipProps {
  emphasis: string;
  type: string;
}

const buildStyles = (props: BadgeStyleProps) => {
  let styles;

  const { emphasis, theme, type } = props;

  const baseStyle = css`
    font-size: 13px;
    font-weight: 700;
    border-radius: 4px;
    min-width: 90px;
    max-width: 180px;
    padding-left: 10px;
    padding-right: 10px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  `;

  if (emphasis === 'subtle') {
    switch (type) {
      case 'default':
        styles = css`
          ${baseStyle};
          background: ${theme.palette.expanded.coolGrey2};
          color: ${theme.palette.expanded.slate};
        `;
        break;
      case 'error':
        styles = css`
          ${baseStyle};
          background: ${theme.palette.expanded.red4};
          color: ${theme.palette.expanded.red2};
        `;
        break;
      case 'success':
        styles = css`
          ${baseStyle};
          background: ${theme.palette.expanded.green4};
          color: ${theme.palette.expanded.green1};
        `;
        break;
      case 'warning':
        styles = css`
          ${baseStyle};
          background: ${theme.palette.expanded.orange3};
          color: ${theme.palette.expanded.orange1};
        `;
        break;
    }
  } else if (emphasis === 'high') {
    switch (type) {
      case 'default':
        styles = css`
          ${baseStyle};
          background: ${theme.palette.expanded.lightSlate};
          color: ${theme.palette.common.white};
        `;
        break;
      case 'error':
        styles = css`
          ${baseStyle};
          background: ${theme.palette.expanded.coral};
          color: ${theme.palette.common.white};
        `;
        break;
      case 'success':
        styles = css`
          ${baseStyle};
          background: ${theme.palette.expanded.green1};
          color: ${theme.palette.common.white};
        `;
        break;
      case 'warning':
        styles = css`
          ${baseStyle};
          background: ${theme.palette.expanded.orange1};
          color: ${theme.palette.common.white};
        `;
        break;
    }
  }

  return styles;
};

export const StyledBadge = styled(MuiChip)`
  ${buildStyles};
`;

Badge.test.tsx

import { ThemeProvider } from '@mui/material/styles';
import { render, screen } from '@testing-library/react';
import React, { ReactNode } from 'react';

import { Badge, BadgeProps } from '../';
import { theme } from '../../../packages/core/src/theme';

const DefaultSubtleArgs: BadgeProps = {
  children: 'Default',
  emphasis: 'subtle',
  type: 'default',
};

const ThemeWrapper = ({ children }: { children: any }) => {
  return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
};

test('renders the badge in the subtle default state', () => {
  render(
    <ThemeWrapper>
      <Badge {...DefaultSubtleArgs} />
    </ThemeWrapper>
  );
  expect(screen.getByTestId('default-subtle-badge')).toHaveTextContent(
    'Default'
  );
});

const SuccessSubtleArgs: BadgeProps = {
  children: 'Success',
  emphasis: 'subtle',
  type: 'success',
};

test('renders the badge in the subtle success state', () => {
  render(
    <ThemeWrapper>
      <Badge {...SuccessSubtleArgs} />
    </ThemeWrapper>
  );
  expect(screen.getByTestId('success-subtle-badge')).toHaveTextContent(
    'Success'
  );
});

const WarningSubtleArgs: BadgeProps = {
  children: 'Warning',
  emphasis: 'subtle',
  type: 'warning',
};

test('renders the badge in the subtle warning state', () => {
  render(
    <ThemeWrapper>
      <Badge {...WarningSubtleArgs} />
    </ThemeWrapper>
  );
  expect(screen.getByTestId('warning-subtle-badge')).toHaveTextContent(
    'Warning'
  );
});

const ErrorSubtleArgs: BadgeProps = {
  children: 'Error',
  emphasis: 'subtle',
  type: 'error',
};

test('renders the badge in the subtle error state', () => {
  render(
    <ThemeWrapper>
      <Badge {...ErrorSubtleArgs} />
    </ThemeWrapper>
  );
  expect(screen.getByTestId('error-subtle-badge')).toHaveTextContent('Error');
});

There is definitely something going on with the way MUI V5 works with Theme Providers. MUI V5 与 Theme Providers 一起工作的方式肯定有问题。

I found a workaround, but it is not elegant.我找到了一个解决方法,但它并不优雅。 Please let me know someone else has another way.请让我知道其他人有其他方法。

With your test, you just stick with wrapping each component with the ThemeProvider and injecting your theme.在您的测试中,您只需坚持使用ThemeProvider包装每个组件并注入您的主题。

In the actual component file, you will need to use the useTheme hook to get the proper theme object pulled in and then you will need to pass it as a unique prop to the styled component.在实际的组件文件中,您需要使用useTheme钩子来获取正确的主题 object,然后您需要将其作为独特的属性传递给样式化的组件。

import { css } from '@emotion/react';
import { Chip as MuiChip, ChipProps as MuiChipProps } from '@mui/material';
import { styled, useTheme, Theme } from '@mui/material/styles';
import React, { ReactElement } from 'react';

export type TBadgeEmphasis = 'subtle' | 'high';
export type TBadgeType = 'default' | 'success' | 'warning' | 'error';

export interface BadgeProps {
  /**
   * Text for the badge.
   */
  children: ReactElement | string;

  /**
   * Option here between `subtle` and `high` depending on the desired intensity.
   */
  emphasis: TBadgeEmphasis;

  /**
   * Option here between `default`, `success`, `warning` and `error`.
   */
  type: string;
}

interface BadgeStyleProps extends MuiChipProps {
  emphasis: string;
  theme: Theme;
  type: string;
}
const StyledBadge = styled(MuiChip)<BadgeStyleProps>(
  ({ emphasis, theme, type }) => {
    let styles;

    const baseStyle = css`
      font-size: 13px;
      font-weight: 700;
      border-radius: 4px;
      min-width: 90px;
      max-width: 180px;
      padding-left: 10px;
      padding-right: 10px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    `;

    if (emphasis === 'subtle') {
      switch (type) {
        case 'default':
          styles = css`
            ${baseStyle};
            background: ${theme.palette.expanded.coolGrey2};
            color: ${theme.palette.expanded.slate};
          `;
          break;
        case 'error':
          styles = css`
            ${baseStyle};
            background: ${theme.palette.expanded.red4};
            color: ${theme.palette.expanded.red2};
          `;
          break;
        case 'success':
          styles = css`
            ${baseStyle};
            background: ${theme.palette.expanded.green4};
            color: ${theme.palette.expanded.green1};
          `;
          break;
        case 'warning':
          styles = css`
            ${baseStyle};
            background: ${theme.palette.expanded.orange3};
            color: ${theme.palette.expanded.orange1};
          `;
          break;
      }
    } else if (emphasis === 'high') {
      switch (type) {
        case 'default':
          styles = css`
            ${baseStyle};
            background: ${theme.palette.expanded.lightSlate};
            color: ${theme.palette.common.white};
          `;
          break;
        case 'error':
          styles = css`
            ${baseStyle};
            background: ${theme.palette.expanded.coral};
            color: ${theme.palette.common.white};
          `;
          break;
        case 'success':
          styles = css`
            ${baseStyle};
            background: ${theme.palette.expanded.green1};
            color: ${theme.palette.common.white};
          `;
          break;
        case 'warning':
          styles = css`
            ${baseStyle};
            background: ${theme.palette.expanded.orange1};
            color: ${theme.palette.common.white};
          `;
          break;
      }
    }

    return styles;
  }
);

export const Badge = (props: BadgeProps): ReactElement => {
  const themeOverride = useTheme();

  return (
    <StyledBadge
      data-testid={`${props.type}-${props.emphasis}-badge`}
      emphasis={props.emphasis}
      label={props.children}
      theme={themeOverride}
      type={props.type}
    />
  );
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM