Skip to content

Utils · classNames

classNames 工具函数常用于合并和控制样式名

使用

测试用例如下

ts
import { test, expect, describe } from 'vitest'
import { cn } from '@slsanyi/utils'

test('(compat) keeps object keys with truthy values', () => {
  const out = cn({ a: true, b: false, c: 0, d: null, e: undefined, f: 1 })
  expect(out).toBe('a f')
})

test('(compat) joins arrays of class names and ignore falsy values', () => {
  const out = cn('a', 0, null, undefined, true, 1, 'b')
  expect(out).toBe('a 1 b')
})

test('(compat) supports heterogenous arguments', () => {
  expect(cn({ a: true }, 'b', 0)).toBe('a b')
})

test('(compat) should be trimmed', () => {
  expect(cn('', 'b', {}, '')).toBe('b')
})

test('(compat) returns an empty string for an empty configuration', () => {
  expect(cn({})).toBe('')
})

test('(compat) supports an array of class names', () => {
  expect(cn(['a', 'b'])).toBe('a b')
})

test('(compat) joins array arguments with string arguments', () => {
  expect(cn(['a', 'b'], 'c')).toBe('a b c')
  expect(cn('c', ['a', 'b'])).toBe('c a b')
})

test('(compat) handles multiple array arguments', () => {
  expect(cn(['a', 'b'], ['c', 'd'])).toBe('a b c d')
})

test('(compat) handles arrays that include falsy and true values', () => {
  expect(cn(['a', 0, null, undefined, false, true, 'b'])).toBe('a b')
})

test('(compat) handles arrays that include arrays', () => {
  expect(cn(['a', ['b', 'c']])).toBe('a b c')
})

test('(compat) handles arrays that include objects', () => {
  expect(cn(['a', { b: true, c: false }])).toBe('a b')
})

test('(compat) handles deep array recursion', () => {
  expect(cn(['a', ['b', ['c', { d: true }]]])).toBe('a b c d')
})

test('(compat) handles arrays that are empty', () => {
  expect(cn('a', [])).toBe('a')
})

test('(compat) handles nested arrays that have empty nested arrays', () => {
  expect(cn('a', [[]])).toBe('a')
})

test('(compat) handles all types of truthy and falsy property values as expected', () => {
  const out = cn({
    // falsy:
    null: null,
    emptyString: '',
    noNumber: NaN,
    zero: 0,
    negativeZero: -0,
    false: false,
    undefined: undefined,

    // truthy (literally anything else):
    nonEmptyString: 'foobar',
    whitespace: ' ',
    function: Object.prototype.toString,
    emptyObject: {},
    nonEmptyObject: { a: 1, b: 2 },
    emptyList: [],
    nonEmptyList: [1, 2, 3],
    greaterZero: 1
  })

  expect(out).toBe(
    'nonEmptyString whitespace function emptyObject nonEmptyObject emptyList nonEmptyList greaterZero'
  )
})


describe('cx', () => {
  describe.each([
    [null, ''],
    [undefined, ''],
    [false && 'foo', ''],
    [true && 'foo', 'foo'],
    [['foo', null, 'bar', undefined, 'baz'], 'foo bar baz'],
    [
      [
        'foo',
        [
          null,
          ['bar'],
          [
            undefined,
            [
              'baz',
              'qux',
              'quux',
              'quuz',
              [[[[[[[[['corge', 'grault']]]]], 'garply']]]]
            ]
          ]
        ]
      ],
      'foo bar baz qux quux quuz corge grault garply'
    ],
    [
      [
        'foo',
        [1 && 'bar', { baz: false, bat: null }, ['hello', ['world']]],
        'cya'
      ],
      'foo bar hello world cya'
    ]
  ])('cx(%o)', (options, expected) => {
    test(`returns ${expected}`, () => {
      expect(cn(options)).toBe(expected)
    })
  })

使用前后对比

实现逻辑