Skip to content

Commit

Permalink
refactor(core): simplify classy (#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
rabelloo authored Dec 17, 2020
1 parent e1727ea commit b18fdfd
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 76 deletions.
26 changes: 6 additions & 20 deletions packages/core/src/utils/classy/classy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { c, classy, m } from './classy';

describe('classy', () => {
const falsy = {
all: [] as any[],
array: ['', undefined, null],
object: { a: false, b: null, c: undefined },
};
falsy.all = [...falsy.array, falsy.object];

it('should work with strings', () => {
const result = classy('foo', 'bar');
Expand Down Expand Up @@ -35,33 +37,17 @@ describe('classy', () => {

describe('c', () => {
it('should apply component prefix', () => {
const result = c('foo', falsy.array, falsy.object, 'bar');
const result = c('foo', ...falsy.all, { bar: true, zed: true });

expect(result.join(' ')).toBe('ods-foo ods-bar');
});

it('should work with tagged template literals', () => {
const result = c`foo bar ${falsy.object} ${
falsy.array
} ${''} ${undefined} ${null} ${'a'}-${{ b: true }}`;

expect(result.join(' ')).toBe('ods-foo ods-bar ods-a-b');
expect(result.join(' ')).toBe('ods-foo ods-bar ods-zed');
});
});

describe('m', () => {
it('should apply modifier prefix', () => {
const result = m('foo', falsy.array, falsy.object, 'bar');

expect(result.join(' ')).toBe('-foo -bar');
});

it('should work with tagged template literals', () => {
const result = m`foo bar ${falsy.object} ${
falsy.array
} ${''} ${undefined} ${null} ${'a'}--${{ b: true }}`;
const result = m('foo', ...falsy.all, { bar: true, zed: true });

expect(result.join(' ')).toBe('-foo -bar -a--b');
expect(result.join(' ')).toBe('-foo -bar -zed');
});
});
});
69 changes: 17 additions & 52 deletions packages/core/src/utils/classy/classy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export type ClassName = string | null | undefined;
export type ClassNameRecord = Record<string, boolean | null | undefined>;
export type ClassNames = ClassName | ClassName[] | ClassNameRecord;
type Template = [TemplateStringsArray, ...ClassNames[]];

/**
* Generates a `className` based on the specified values.
Expand All @@ -19,73 +18,39 @@ type Template = [TemplateStringsArray, ...ClassNames[]];
* // 'foo-1g4k53 global-1 global-2 className disabled'
*/
export const classy = (...classNames: ClassNames[]) =>
classNames
.flat(10)
.flatMap((className) =>
className instanceof Object ? getTruthyKeys(className) : className
)
.filter(Boolean)
.join(' ');
classNames.flat(10).flatMap(truthyNames).join(' ');

/**
* Prefixes component names with a predetermined identifier.
*
* Can be used as tagged template literal, separated by spaces.
*
* @param components Names to prefix.
*
* @example
* c('button') || c`button`;
* c('button');
* // ['ods-button']
*/
export const c = mapWithPrefix('ods-');
export const c = withPrefix('ods-');

/**
* Prefixes modifier names with a predetermined identifier.
*
* Can be used as tagged template literal, separated by spaces.
*
* @param modifiers Names to prefix.
*
* @example
* m('action', 'primary') || m`action primary`;
* m('action', 'primary');
* // ['-action', '-primary']
*/
export const m = mapWithPrefix('-');

const fromTemplate = (array: Template | ClassNames[]) =>
isTemplate(array)
? array[0]
.map((value, i) => value + classy(array[i + 1] as ClassNames))
.join('')
.split(' ')
: array;

const getTruthyKeys = (obj: Record<string, unknown>): string[] =>
Object.entries(obj)
.filter(([, value]) => !!value)
.map(([key]) => key);

const isTemplate = (template: Template | unknown[]): template is Template =>
Array.isArray(template[0]) &&
Array.isArray(((template[0] as unknown) as TemplateStringsArray).raw);

const mapKey = <V>(
obj: Record<string, V>,
mapFn: (key: string) => string
): Record<string, V> =>
Object.fromEntries(
Object.entries(obj).map(([key, value]) => [mapFn(key), value])
);

function mapWithPrefix(prefix: string) {
return (...array: ClassNames[] | Template) =>
fromTemplate(array)
.flat(10)
.filter(Boolean)
.flatMap((className) =>
className instanceof Object
? getTruthyKeys(mapKey(className, (key) => `${prefix}${key}`))
: `${prefix}${className}`
);
export const m = withPrefix('-');

const truthyNames = (value: ClassName | ClassNameRecord) =>
(value instanceof Object
? Object.entries(value)
.filter(([, value]) => !!value)
.map(([key]) => key)
: [value]
).filter(Boolean);

function withPrefix(prefix: string) {
return (...classNames: (ClassName | ClassNameRecord)[]) =>
classNames.flatMap(truthyNames).map((name) => `${prefix}${name}`);
}
2 changes: 1 addition & 1 deletion packages/react/src/button/button.react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const Button: ButtonComponent = ({
<Element
{...(restProps as HTMLAttributes<HTMLElement>)}
{...(Element === 'a' && { role: 'button' })}
className={classy(c`button`, m`${kind}--${variant}`, className)}
className={classy(c('button'), m(`${kind}--${variant}`), className)}
/>
);
};
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/icon/icon.react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const Icon = ({
{...restProps}
fill={token ? color(token) : 'currentColor'}
focusable="false"
className={classy(c`icon`, className)}
className={classy(c('icon'), className)}
>
<use href={`#${name}`}></use>
</svg>
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/input/input.react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const Input = ({
<input
{...restProps}
type={type}
className={classy(c`input`, m({ invalid }), className)}
className={classy(c('input'), m({ invalid }), className)}
/>
);

Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/textarea/textarea.react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const Textarea = ({
<textarea
{...restProps}
rows={rows}
className={classy(c`textarea`, m({ invalid }), className)}
className={classy(c('textarea'), m({ invalid }), className)}
style={{ ...style, resize }}
/>
);
Expand Down

0 comments on commit b18fdfd

Please sign in to comment.