Skip to content

Commit

Permalink
WIP rewrite in ts
Browse files Browse the repository at this point in the history
  • Loading branch information
VacaSan committed Jan 23, 2023
1 parent 78e3869 commit 2f1b957
Show file tree
Hide file tree
Showing 11 changed files with 5,381 additions and 14,044 deletions.
3 changes: 0 additions & 3 deletions .babelrc

This file was deleted.

19,103 changes: 5,208 additions & 13,895 deletions package-lock.json

Large diffs are not rendered by default.

35 changes: 17 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
"name": "@vacasan/classified",
"version": "0.0.3-alpha.0",
"description": "",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"main": "dist/classified.js",
"module": "dist/classified.esm.js",
"types": "dist/classified.d.ts",
"files": [
"dist/*"
],
Expand All @@ -12,30 +13,28 @@
"url": "https://github.com/VacaSan/classified"
},
"scripts": {
"build": "rollup -c",
"test": "jest"
"build": "tsup src/classified.ts --format cjs,esm --dts",
"lint": "tsc",
"test": "vitest"
},
"author": "Vladan Kotarac",
"license": "MIT",
"peerDependencies": {
"react": ">=16.13.1"
},
"devDependencies": {
"@babel/cli": "^7.10.5",
"@babel/core": "^7.11.1",
"@babel/preset-env": "^7.11.0",
"@babel/preset-react": "^7.10.4",
"@rollup/plugin-commonjs": "^15.0.0",
"@rollup/plugin-node-resolve": "^9.0.0",
"jest": "^26.4.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-test-renderer": "^16.13.1",
"rollup": "^2.26.2",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-peer-deps-external": "^2.2.3"
"@changesets/cli": "^2.26.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@types/react": "^18.0.27",
"@vitejs/plugin-react": "^3.0.1",
"jsdom": "^21.1.0",
"react": "^18.2.0",
"tsup": "^6.5.0",
"typescript": "^4.9.4",
"vitest": "^0.28.1"
},
"dependencies": {
"@emotion/is-prop-valid": "^0.8.8"
"@emotion/is-prop-valid": "^1.2.0"
}
}
29 changes: 0 additions & 29 deletions rollup.config.js

This file was deleted.

1 change: 1 addition & 0 deletions setup-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import "@testing-library/jest-dom";
46 changes: 0 additions & 46 deletions src/classified.js

This file was deleted.

53 changes: 0 additions & 53 deletions src/classified.test.js

This file was deleted.

77 changes: 77 additions & 0 deletions src/classified.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { HTMLProps } from "react";
import { render, screen } from "@testing-library/react";
import { classified } from "./classified";

type Props = { variant?: "primary"; block?: boolean };

test("should create a component", () => {
let Button = classified("button")([]);
render(<Button>hello</Button>);
expect(screen.getByRole("button", { name: /hello/i })).toBeInTheDocument();
});

test("should apply string classNames", () => {
let Button = classified("button")(["btn"]);
render(<Button>hello</Button>);
expect(screen.getByRole("button", { name: /hello/i }).className).toBe("btn");
});

test("should apply dynamic classNames", () => {
let Button = classified("button")([() => "btn"]);
render(<Button>hello</Button>);
expect(screen.getByRole("button", { name: /hello/i }).className).toBe("btn");
});

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

test("should accept any react component as the first argument", () => {
function Link(props: HTMLProps<HTMLAnchorElement>) {
return <a {...props} />;
}

let ClassifiedLink = classified(Link)(["link"]);
render(<ClassifiedLink href="/read-more">Read more</ClassifiedLink>);
expect(screen.getByRole("link", { name: /read more/i }).className).toBe(
"link"
);
});

test("component should accept any additional `className`", () => {
let Button = classified("button")(["btn"]);
render(<Button className="special">hello</Button>);
expect(screen.getByRole("button", { name: /hello/i }).className).toBe(
"btn special"
);
});

test("component should accept `as` prop", () => {
let Button = classified("button")(["btn"]);
render(
<Button as="a" href="/">
hello
</Button>
);
expect(screen.getByRole("link", { name: /hello/i }).className).toBe("btn");
});
50 changes: 50 additions & 0 deletions src/classified.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { ComponentProps, ElementType, HTMLProps } 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) {
let className = names
.concat(props.className)
.map(name => (isFunc(name) ? name(props) : name))
.filter(Boolean)
.join(" ");

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

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

return forwardRef(ClassifiedComponent);
};
}

function cx(names: Array<string | Falsy> = []) {
return names.filter(Boolean).join(" ");
}

function isFunc(x: unknown): x is Function {
return typeof x === "function";
}

function isString(x: unknown): x is string {
return typeof x === "string";
}

function getHtmlProps(props: Record<string, any>) {
return Object.keys(props).reduce((htmlProps, prop) => {
if (isPropValid(prop)) {
htmlProps[prop] = props[prop];
}
return htmlProps;
}, {} as Partial<typeof props>);
}

export { classified, cx };
14 changes: 14 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"types": ["vitest/globals"],
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"noUncheckedIndexedAccess": true,
"noEmit": true,
"jsx": "preserve"
}
}
14 changes: 14 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/// <reference types="vitest" />
/// <reference types="vite/client" />

import react from "@vitejs/plugin-react";
import { defineConfig } from "vitest/config";

export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: "jsdom",
setupFiles: "./setup-test.ts",
},
});

0 comments on commit 2f1b957

Please sign in to comment.