diff --git a/packages/styles/package.json b/packages/styles/package.json index 41dfba24..9e13e6cf 100644 --- a/packages/styles/package.json +++ b/packages/styles/package.json @@ -2,20 +2,42 @@ "name": "@firebase-ui/styles", "version": "0.0.1", "type": "module", + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.cjs" + } + }, "files": [ "dist.css", "src" ], "scripts": { "prepare": "pnpm run build", - "build": "npx -y @tailwindcss/cli -i ./src.css -o ./dist.css --minify", + "build": "tsup && pnpm run build:css", + "build:css": "npx -y @tailwindcss/cli -i ./src.css -o ./dist.css --minify", "build:local": "pnpm run build && pnpm pack", - "test": "echo \"No tests specified\" && exit 0", - "test:watch": "echo \"No tests specified\" && exit 0", + "lint": "tsc --noEmit", + "format": "prettier --write \"src/**/*.ts\"", + "clean": "rimraf dist", + "test": "vitest run", + "test:watch": "vitest", "publish:tags": "sh -c 'TAG=\"${npm_package_name}@${npm_package_version}\"; git tag --list \"$TAG\" | grep . || git tag \"$TAG\"; git push origin \"$TAG\"'", "release": "pnpm run build && pnpm pack --pack-destination ../../releases/" }, "devDependencies": { - "tailwindcss": "catalog:" + "prettier": "catalog:", + "rimraf": "catalog:", + "tailwindcss": "catalog:", + "tsup": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" + }, + "dependencies": { + "cva": "1.0.0-beta.4" } } diff --git a/packages/styles/src/index.test.ts b/packages/styles/src/index.test.ts new file mode 100644 index 00000000..34f644ea --- /dev/null +++ b/packages/styles/src/index.test.ts @@ -0,0 +1,65 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE/2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, it, expect } from "vitest"; +import { buttonVariant, type ButtonVariant } from "./index"; + +describe("buttonVariant", () => { + it("should return base class when no variant is provided", () => { + const result = buttonVariant(); + expect(result).toBe("fui-button"); + }); + + it("should return base class with primary variant (default)", () => { + const result = buttonVariant({ variant: "primary" }); + expect(result).toBe("fui-button"); + }); + + it("should return base class with secondary variant", () => { + const result = buttonVariant({ variant: "secondary" }); + expect(result).toBe("fui-button fui-button--secondary"); + }); + + it("should handle empty variant object", () => { + const result = buttonVariant({}); + expect(result).toBe("fui-button"); + }); + + it("should handle undefined variant", () => { + const result = buttonVariant({ variant: undefined }); + expect(result).toBe("fui-button"); + }); +}); + +describe("ButtonVariant type", () => { + it("should accept valid variant values", () => { + const primaryVariant: ButtonVariant = "primary"; + const secondaryVariant: ButtonVariant = "secondary"; + + expect(primaryVariant).toBe("primary"); + expect(secondaryVariant).toBe("secondary"); + }); + + it("should work with buttonVariant function", () => { + const variants: ButtonVariant[] = ["primary", "secondary"]; + + variants.forEach((variant) => { + const result = buttonVariant({ variant }); + expect(typeof result).toBe("string"); + expect(result).toContain("fui-button"); + }); + }); +}); diff --git a/packages/styles/src/index.ts b/packages/styles/src/index.ts new file mode 100644 index 00000000..b5ee3b46 --- /dev/null +++ b/packages/styles/src/index.ts @@ -0,0 +1,16 @@ +import { cva, type VariantProps } from "cva"; + +export const buttonVariant = cva({ + base: "fui-button", + variants: { + variant: { + primary: "", + secondary: "fui-button--secondary", + }, + }, + defaultVariants: { + variant: "primary", + }, +}); + +export type ButtonVariant = VariantProps['variant']; diff --git a/packages/styles/tsconfig.json b/packages/styles/tsconfig.json new file mode 100644 index 00000000..c3f99549 --- /dev/null +++ b/packages/styles/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "lib": ["ES2020", "DOM"], + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "useUnknownInCatchVariables": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "exactOptionalPropertyTypes": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "moduleResolution": "node" + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/styles/tsup.config.ts b/packages/styles/tsup.config.ts new file mode 100644 index 00000000..f37dc411 --- /dev/null +++ b/packages/styles/tsup.config.ts @@ -0,0 +1,30 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defineConfig, type Options } from "tsup"; + +const config: Options = { + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + splitting: false, + sourcemap: true, + clean: true, + treeshake: true, + minify: true, +}; + +export default defineConfig(config); diff --git a/packages/styles/vitest.config.ts b/packages/styles/vitest.config.ts new file mode 100644 index 00000000..5d630b13 --- /dev/null +++ b/packages/styles/vitest.config.ts @@ -0,0 +1,28 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE/2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + // Use the same environment as the package + environment: "jsdom", + // Include TypeScript files + include: ["src/**/*.{test,spec}.{js,ts}"], + // Exclude build output and node_modules + exclude: ["node_modules/**/*", "dist/**/*"], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ba68a1a..2f051ee7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -368,10 +368,29 @@ importers: version: 3.4.1 packages/styles: + dependencies: + cva: + specifier: 1.0.0-beta.4 + version: 1.0.0-beta.4(typescript@5.9.2) devDependencies: + prettier: + specifier: 'catalog:' + version: 3.6.2 + rimraf: + specifier: 'catalog:' + version: 6.0.1 tailwindcss: specifier: 'catalog:' version: 4.1.13 + tsup: + specifier: 'catalog:' + version: 8.5.0(@microsoft/api-extractor@7.52.12(@types/node@24.3.1))(jiti@1.21.7)(postcss@8.5.6)(typescript@5.9.2) + typescript: + specifier: 'catalog:' + version: 5.9.2 + vitest: + specifier: 'catalog:' + version: 3.2.4(@types/node@24.3.1)(jiti@1.21.7)(jsdom@26.1.0)(less@4.4.1)(sass@1.92.1)(terser@5.43.1) packages/translations: devDependencies: @@ -3515,6 +3534,14 @@ packages: custom-event@1.0.1: resolution: {integrity: sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==} + cva@1.0.0-beta.4: + resolution: {integrity: sha512-F/JS9hScapq4DBVQXcK85l9U91M6ePeXoBMSp7vypzShoefUBxjQTo3g3935PUHgQd+IW77DjbPRIxugy4/GCQ==} + peerDependencies: + typescript: '>= 4.5.5' + peerDependenciesMeta: + typescript: + optional: true + data-uri-to-buffer@3.0.1: resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==} engines: {node: '>= 6'} @@ -9635,22 +9662,22 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1))': + '@vitest/mocker@3.2.4(vite@6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1) + vite: 6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1) optional: true - '@vitest/mocker@3.2.4(vite@7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1))': + '@vitest/mocker@3.2.4(vite@6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.19 optionalDependencies: - vite: 7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1) + vite: 6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1) '@vitest/pretty-format@3.2.4': dependencies: @@ -10423,6 +10450,12 @@ snapshots: custom-event@1.0.1: {} + cva@1.0.0-beta.4(typescript@5.9.2): + dependencies: + clsx: 2.1.1 + optionalDependencies: + typescript: 5.9.2 + data-uri-to-buffer@3.0.1: {} data-urls@5.0.0: @@ -13764,7 +13797,7 @@ snapshots: debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1) + vite: 6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1) transitivePeerDependencies: - '@types/node' - jiti @@ -13786,7 +13819,7 @@ snapshots: debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1) + vite: 6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1) transitivePeerDependencies: - '@types/node' - jiti @@ -13831,7 +13864,7 @@ snapshots: - supports-color - typescript - vite@6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1): + vite@6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) @@ -13843,27 +13876,28 @@ snapshots: '@types/node': 24.3.1 fsevents: 2.3.3 jiti: 1.21.7 - less: 4.4.1 - sass: 1.92.1 + less: 4.4.0 + sass: 1.90.0 terser: 5.43.1 + optional: true - vite@7.1.2(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1): + vite@6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 rollup: 4.50.1 - tinyglobby: 0.2.14 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.3.1 fsevents: 2.3.3 jiti: 1.21.7 - less: 4.4.0 - sass: 1.90.0 + less: 4.4.1 + sass: 1.92.1 terser: 5.43.1 - vite@7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1): + vite@7.1.2(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1): dependencies: esbuild: 0.25.9 fdir: 6.5.0(picomatch@4.0.3) @@ -13878,7 +13912,6 @@ snapshots: less: 4.4.0 sass: 1.90.0 terser: 5.43.1 - optional: true vite@7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1): dependencies: @@ -13909,7 +13942,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1)) + '@vitest/mocker': 3.2.4(vite@6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -13927,7 +13960,7 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1) + vite: 6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1) vite-node: 3.2.4(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.0)(sass@1.90.0)(terser@5.43.1) why-is-node-running: 2.3.0 optionalDependencies: @@ -13952,7 +13985,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1)) + '@vitest/mocker': 3.2.4(vite@6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -13970,7 +14003,7 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.1.5(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1) + vite: 6.3.6(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1) vite-node: 3.2.4(@types/node@24.3.1)(jiti@1.21.7)(less@4.4.1)(sass@1.92.1)(terser@5.43.1) why-is-node-running: 2.3.0 optionalDependencies: