Skip to content

Commit

Permalink
add some type support
Browse files Browse the repository at this point in the history
  • Loading branch information
VKotarac committed Jan 24, 2023
1 parent 2f1b957 commit 5583f19
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 14 deletions.
10 changes: 6 additions & 4 deletions src/classified.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,20 @@ test("should apply dynamic classNames", () => {
});

test("should pass props to dynamic className creator", () => {
let Button = classified("button")([({ variant }: Props) => `btn-${variant}`]);
let Button = classified<"button", Props>("button")([
({ variant }) => `btn-${variant}`,
]);
render(<Button variant="primary">hello</Button>);
expect(screen.getByRole("button", { name: /hello/i }).className).toBe(
"btn-primary"
);
});

test("should combine both static and dynamic classNames", () => {
let Button = classified("button")([
let Button = classified<"button", Props>("button")([
"btn",
({ variant }: Props) => `btn-${variant}`,
({ block }: Props) => block && "btn-block",
({ variant }) => `btn-${variant}`,
({ block }) => block && "btn-block",
]);
render(
<Button variant="primary" block>
Expand Down
33 changes: 23 additions & 10 deletions src/classified.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
import type { ComponentProps, ElementType, HTMLProps } from "react";
import type { ComponentProps, ElementType } from "react";
import { createElement, forwardRef } from "react";
import isPropValid from "@emotion/is-prop-valid";

type Falsy = false | 0 | "" | null | undefined;

function classified(type) {
return function createClassifiedComponent(names) {
function ClassifiedComponent({ as = type, children, ...props }, ref) {
type Generator<Props> = ((props: Props) => string | Falsy) | string | Falsy;

type ClassNames<Props> = Array<Generator<Props>>;

function classified<
Type extends ElementType,
AdditionalProps extends object = {}
>(type: Type) {
type GeneratorProps = AdditionalProps & ComponentProps<Type>;

return function createClassifiedComponent(names: ClassNames<GeneratorProps>) {
type Props = GeneratorProps & {
as?: Type;
className?: string;
};

const ClassifiedComponent = forwardRef<Type, Props>((props: Props, ref) => {
let className = names
.concat(props.className)
.map(name => (isFunc(name) ? name(props) : name))
.filter(Boolean)
.join(" ");

let elementProps = isString(as) ? getHtmlProps(props) : props;
let elementProps = isString(props.as) ? getHtmlProps(props) : props;

return createElement(
as,
Object.assign(elementProps, { className, ref }),
children
props.as || type,
Object.assign({}, elementProps, { className, ref })
);
}
});

return forwardRef(ClassifiedComponent);
return ClassifiedComponent;
};
}

Expand Down

0 comments on commit 5583f19

Please sign in to comment.