Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] Adding a matcher to specify multiple style rules for a given modifier/element #392

Open
AllySummers opened this issue Sep 22, 2021 · 0 comments

Comments

@AllySummers
Copy link

Hi!

I'd love it if we can add another matcher to jest-styled-components to support checking multiple style rules on the one element/modifier settings.

The Problem

Lets say I have the following component created with Styled Components:

interface TableStyle {
  readonly altStyle?: boolean;
}

/* BSTable = Bootstrap's Table */
const StyledTable = styled(BSTable)<TableStyle>`
  ${props => props.altStyle && css`
    th,
    td {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
      line-height: normal;
      height: 12px;
    }

    th {
      font-size: 14px;
      font-weight: 500;
    }

    td {
      font-size: 13px;
      font-weight: 400;
    }
  `}
`;

When the altStyle attribute is true, it will apply some styles to both nested th and td elements, some styles only apply to nested th and others only apply to nested td.

When testing this to ensure all the style rules are set, it is very repetitive to check for multiple rules on the same component. When using only jest-styled-components, we'd be looking at this:

describe('Table Component', () => {
  it('Adds the expected rules when property is true', () => {
    const props = {
      property: true
    };

    const tree = create(<Table { ...props } />).toJSON();

    expect(tree).toHaveStyleRule('font-family', expect.stringContaining('apple-system'), { modifier: 'th' });
    expect(tree).toHaveStyleRule('font-family', expect.stringContaining('apple-system'), { modifier: 'td' });
    expect(tree).toHaveStyleRule('line-height', 'normal', { modifier: 'th' });
    expect(tree).toHaveStyleRule('line-height', 'normal', { modifier: 'td' });
    expect(tree).toHaveStyleRule('height', '12px', { modifier: 'th' });
    expect(tree).toHaveStyleRule('height', '12px', { modifier: 'td' });
    expect(tree).toHaveStyleRule('font-size', '14px', { modifier: 'th' });
    expect(tree).toHaveStyleRule('font-size', '13px', { modifier: 'td' });
    expect(tree).toHaveStyleRule('font-weight', '500', { modifier: 'th' });
    expect(tree).toHaveStyleRule('font-weight', '400', { modifier: 'td' });
  });
});

Now, if we add in jest-chain it gets slightly less repetitive, but still a lot of duplication. Like so:

describe('Table Component', () => {
  it('Adds the expected rules when property is true', () => {
    const props = {
      property: true
    };

    const tree = create(<Table { ...props } />).toJSON();

    expect(tree)
      .toHaveStyleRule('font-family', expect.stringContaining('apple-system'), { modifier: 'th' })
      .toHaveStyleRule('font-family', expect.stringContaining('apple-system'), { modifier: 'td' })
      .toHaveStyleRule('line-height', 'normal', { modifier: 'th' })
      .toHaveStyleRule('line-height', 'normal', { modifier: 'td' })
      .toHaveStyleRule('height', '12px', { modifier: 'th' })
      .toHaveStyleRule('height', '12px', { modifier: 'td' })
      .toHaveStyleRule('font-size', '14px', { modifier: 'th' })
      .toHaveStyleRule('font-size', '13px', { modifier: 'td' })
      .toHaveStyleRule('font-weight', '500', { modifier: 'th' })
      .toHaveStyleRule('font-weight', '400', { modifier: 'td' });
  });
});

What I'm proposing

It would be great if we could add a matcher, very similar to the current toHaveStyleRule, but named something along the lines of toHaveStyleRules. Here is the proposed TypeScript API (alongside the current one):

declare global {
  namespace jest {
    interface AsymmetricMatcher {
      $$typeof: Symbol;
      sample?: string | RegExp | object | Array<any> | Function;
    }

    type Value = string | number | RegExp | AsymmetricMatcher | undefined;

    interface Options {
      media?: string;
      modifier?: string;
      supports?: string;
    }

    type MatchRule = [
      property: string,
      value?: Value
    ]

    interface Matchers<R, T> {
      toHaveStyleRule(property: string, value?: Value, options?: Options): R;
      toHaveStyleRules(rules: Array<MatchRule>, options?: Options): R;
    }
  }
}

As you can see in the above code, it is almost the same as toHaveStyleRule, but matching multiple rules on the one element/modifier/etc.
It would look something like this:

describe('Table Component', () => {
  it('Adds the expected rules when property is true', () => {
    const props = {
      property: true
    };

    const tree = create(<Table { ...props } />).toJSON();

    const commonRules = [
      ['font-family', expect.stringContaining('apple-system')],
      ['line-height', 'normal'],
      ['height', '12px']
    ];

    expect(tree).toHaveStyleRules([
      ...commonRules,
      ['font-size', '14px'],
      ['font-weight', '500']
    ], { modifier: 'th' });

    expect(tree).toHaveStyleRules([
      ...commonRules,
      ['font-size', '13px'],
      ['font-weight', '400']
    ], { modifier: 'td' });
  });
});

This would allow us to allow code re-use more easily and reduce repetition.

I'm happy to create a PR for this change as it should be fairly simple and could almost certainly use the existing toHaveStyleRule function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant