From 18896ebdb3fddf63da1641dd7bb35cd82460c921 Mon Sep 17 00:00:00 2001 From: Ross Searle Date: Mon, 27 Apr 2026 05:21:16 +0100 Subject: [PATCH 1/4] feat: add ExRegisterBox UI component for user registration Introduces a new Stencil web component, `ExRegisterBox`, which provides a complete UI for user registration. This component includes: - Email, password, and confirm password input fields. - Password visibility toggling. - Password confirmation validation. - An event for form submission (`exSubmit`). - An event for navigating to the sign-in page (`exSignIn`). - A slot for integrating social login buttons. - Full integration with the existing theming system. The component is exposed in the `@expressthat-auth/ui-react` package and demonstrated in the example `apps/web` application. The `@expressthat-auth/api-client` is added as a dev dependency to the `ui` package, anticipating its use in future component logic. --- apps/web/src/app/page.tsx | 6 +- packages/ui/package.json | 1 + packages/ui/src/components.d.ts | 119 +++++++++++ .../ui/src/components/ex-button/readme.md | 2 + packages/ui/src/components/ex-input/readme.md | 2 + .../ex-register-box/ex-register-box.css | 3 + .../ex-register-box/ex-register-box.tsx | 200 ++++++++++++++++++ .../src/components/ex-register-box/readme.md | 72 +++++++ packages/ui/src/index.ts | 1 + pnpm-lock.yaml | 3 + 10 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 packages/ui/src/components/ex-register-box/ex-register-box.css create mode 100644 packages/ui/src/components/ex-register-box/ex-register-box.tsx create mode 100644 packages/ui/src/components/ex-register-box/readme.md diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 259053a..3b173eb 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -1,7 +1,7 @@ "use client"; import { Button } from "@expressthat-auth/internal-components/button"; -import { ExLoginBox, ExTestButton } from "@expressthat-auth/ui-react"; +import { ExLoginBox, ExRegisterBox, ExTestButton } from "@expressthat-auth/ui-react"; export default function Home() { return ( @@ -20,6 +20,10 @@ export default function Home() {

test

+ + +

test

+
); } diff --git a/packages/ui/package.json b/packages/ui/package.json index ec59b31..a65606d 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -51,6 +51,7 @@ }, "devDependencies": { "@expressthat-auth/typescript-config": "workspace:*", + "@expressthat-auth/api-client": "workspace:*", "@stencil/angular-output-target": "^0.10.0", "@stencil/core": "^4.30.0", "@stencil/react-output-target": "^1.2.0", diff --git a/packages/ui/src/components.d.ts b/packages/ui/src/components.d.ts index 286e58d..e8900d5 100644 --- a/packages/ui/src/components.d.ts +++ b/packages/ui/src/components.d.ts @@ -6,7 +6,9 @@ */ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; import { EXLoginBoxSubmitDetail } from "./components/ex-login-box/ex-login-box"; +import { EXRegisterBoxSubmitDetail } from "./components/ex-register-box/ex-register-box"; export { EXLoginBoxSubmitDetail } from "./components/ex-login-box/ex-login-box"; +export { EXRegisterBoxSubmitDetail } from "./components/ex-register-box/ex-register-box"; export namespace Components { interface ExButton { "colorBackground"?: string; @@ -152,6 +154,34 @@ export namespace Components { "radiusSm"?: string; "radiusXl"?: string; } + interface ExRegisterBox { + "colorBackground"?: string; + "colorDivider"?: string; + "colorError"?: string; + "colorInputBackground"?: string; + "colorInputBorder"?: string; + "colorInputBorderFocus"?: string; + "colorLink"?: string; + "colorPrimary"?: string; + "colorPrimaryForeground"?: string; + "colorPrimaryHover"?: string; + "colorSurface"?: string; + "colorText"?: string; + "colorTextMuted"?: string; + "fontFamily"?: string; + "fontSizeLg"?: string; + "fontSizeMd"?: string; + "fontSizeSm"?: string; + "fontSizeXl"?: string; + "fontSizeXs"?: string; + "fontWeightMedium"?: string; + "fontWeightNormal"?: string; + "fontWeightSemibold"?: string; + "radiusLg"?: string; + "radiusMd"?: string; + "radiusSm"?: string; + "radiusXl"?: string; + } interface ExTestButton { /** * Whether the button is disabled. @@ -182,6 +212,10 @@ export interface ExLoginBoxCustomEvent extends CustomEvent { detail: T; target: HTMLExLoginBoxElement; } +export interface ExRegisterBoxCustomEvent extends CustomEvent { + detail: T; + target: HTMLExRegisterBoxElement; +} export interface ExTestButtonCustomEvent extends CustomEvent { detail: T; target: HTMLExTestButtonElement; @@ -240,6 +274,24 @@ declare global { prototype: HTMLExLoginBoxElement; new (): HTMLExLoginBoxElement; }; + interface HTMLExRegisterBoxElementEventMap { + "exSubmit": EXRegisterBoxSubmitDetail; + "exSignIn": void; + } + interface HTMLExRegisterBoxElement extends Components.ExRegisterBox, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLExRegisterBoxElement, ev: ExRegisterBoxCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLExRegisterBoxElement, ev: ExRegisterBoxCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; + } + var HTMLExRegisterBoxElement: { + prototype: HTMLExRegisterBoxElement; + new (): HTMLExRegisterBoxElement; + }; interface HTMLExTestButtonElementEventMap { "exTestClick": void; } @@ -261,6 +313,7 @@ declare global { "ex-button": HTMLExButtonElement; "ex-input": HTMLExInputElement; "ex-login-box": HTMLExLoginBoxElement; + "ex-register-box": HTMLExRegisterBoxElement; "ex-test-button": HTMLExTestButtonElement; } } @@ -429,6 +482,42 @@ declare namespace LocalJSX { "radiusSm"?: string; "radiusXl"?: string; } + interface ExRegisterBox { + "colorBackground"?: string; + "colorDivider"?: string; + "colorError"?: string; + "colorInputBackground"?: string; + "colorInputBorder"?: string; + "colorInputBorderFocus"?: string; + "colorLink"?: string; + "colorPrimary"?: string; + "colorPrimaryForeground"?: string; + "colorPrimaryHover"?: string; + "colorSurface"?: string; + "colorText"?: string; + "colorTextMuted"?: string; + "fontFamily"?: string; + "fontSizeLg"?: string; + "fontSizeMd"?: string; + "fontSizeSm"?: string; + "fontSizeXl"?: string; + "fontSizeXs"?: string; + "fontWeightMedium"?: string; + "fontWeightNormal"?: string; + "fontWeightSemibold"?: string; + /** + * Fired when the "Sign in" link is clicked. + */ + "onExSignIn"?: (event: ExRegisterBoxCustomEvent) => void; + /** + * Fired when the form is submitted; detail contains email + password. + */ + "onExSubmit"?: (event: ExRegisterBoxCustomEvent) => void; + "radiusLg"?: string; + "radiusMd"?: string; + "radiusSm"?: string; + "radiusXl"?: string; + } interface ExTestButton { /** * Whether the button is disabled. @@ -548,6 +637,34 @@ declare namespace LocalJSX { "radiusLg": string; "radiusXl": string; } + interface ExRegisterBoxAttributes { + "colorBackground": string; + "colorSurface": string; + "colorPrimary": string; + "colorPrimaryHover": string; + "colorPrimaryForeground": string; + "colorText": string; + "colorTextMuted": string; + "colorInputBackground": string; + "colorInputBorder": string; + "colorInputBorderFocus": string; + "colorError": string; + "colorDivider": string; + "colorLink": string; + "fontFamily": string; + "fontSizeXs": string; + "fontSizeSm": string; + "fontSizeMd": string; + "fontSizeLg": string; + "fontSizeXl": string; + "fontWeightNormal": string; + "fontWeightMedium": string; + "fontWeightSemibold": string; + "radiusSm": string; + "radiusMd": string; + "radiusLg": string; + "radiusXl": string; + } interface ExTestButtonAttributes { "label": string; "variant": "primary" | "secondary" | "outline"; @@ -558,6 +675,7 @@ declare namespace LocalJSX { "ex-button": Omit & { [K in keyof ExButton & keyof ExButtonAttributes]?: ExButton[K] } & { [K in keyof ExButton & keyof ExButtonAttributes as `attr:${K}`]?: ExButtonAttributes[K] } & { [K in keyof ExButton & keyof ExButtonAttributes as `prop:${K}`]?: ExButton[K] }; "ex-input": Omit & { [K in keyof ExInput & keyof ExInputAttributes]?: ExInput[K] } & { [K in keyof ExInput & keyof ExInputAttributes as `attr:${K}`]?: ExInputAttributes[K] } & { [K in keyof ExInput & keyof ExInputAttributes as `prop:${K}`]?: ExInput[K] }; "ex-login-box": Omit & { [K in keyof ExLoginBox & keyof ExLoginBoxAttributes]?: ExLoginBox[K] } & { [K in keyof ExLoginBox & keyof ExLoginBoxAttributes as `attr:${K}`]?: ExLoginBoxAttributes[K] } & { [K in keyof ExLoginBox & keyof ExLoginBoxAttributes as `prop:${K}`]?: ExLoginBox[K] }; + "ex-register-box": Omit & { [K in keyof ExRegisterBox & keyof ExRegisterBoxAttributes]?: ExRegisterBox[K] } & { [K in keyof ExRegisterBox & keyof ExRegisterBoxAttributes as `attr:${K}`]?: ExRegisterBoxAttributes[K] } & { [K in keyof ExRegisterBox & keyof ExRegisterBoxAttributes as `prop:${K}`]?: ExRegisterBox[K] }; "ex-test-button": Omit & { [K in keyof ExTestButton & keyof ExTestButtonAttributes]?: ExTestButton[K] } & { [K in keyof ExTestButton & keyof ExTestButtonAttributes as `attr:${K}`]?: ExTestButtonAttributes[K] } & { [K in keyof ExTestButton & keyof ExTestButtonAttributes as `prop:${K}`]?: ExTestButton[K] }; } } @@ -568,6 +686,7 @@ declare module "@stencil/core" { "ex-button": LocalJSX.IntrinsicElements["ex-button"] & JSXBase.HTMLAttributes; "ex-input": LocalJSX.IntrinsicElements["ex-input"] & JSXBase.HTMLAttributes; "ex-login-box": LocalJSX.IntrinsicElements["ex-login-box"] & JSXBase.HTMLAttributes; + "ex-register-box": LocalJSX.IntrinsicElements["ex-register-box"] & JSXBase.HTMLAttributes; "ex-test-button": LocalJSX.IntrinsicElements["ex-test-button"] & JSXBase.HTMLAttributes; } } diff --git a/packages/ui/src/components/ex-button/readme.md b/packages/ui/src/components/ex-button/readme.md index eb925ba..72b6f41 100644 --- a/packages/ui/src/components/ex-button/readme.md +++ b/packages/ui/src/components/ex-button/readme.md @@ -61,11 +61,13 @@ ### Used by - [ex-login-box](../ex-login-box) + - [ex-register-box](../ex-register-box) ### Graph ```mermaid graph TD; ex-login-box --> ex-button + ex-register-box --> ex-button style ex-button fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/packages/ui/src/components/ex-input/readme.md b/packages/ui/src/components/ex-input/readme.md index aa978c5..61373ca 100644 --- a/packages/ui/src/components/ex-input/readme.md +++ b/packages/ui/src/components/ex-input/readme.md @@ -66,11 +66,13 @@ ### Used by - [ex-login-box](../ex-login-box) + - [ex-register-box](../ex-register-box) ### Graph ```mermaid graph TD; ex-login-box --> ex-input + ex-register-box --> ex-input style ex-input fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/packages/ui/src/components/ex-register-box/ex-register-box.css b/packages/ui/src/components/ex-register-box/ex-register-box.css new file mode 100644 index 0000000..5d4e87f --- /dev/null +++ b/packages/ui/src/components/ex-register-box/ex-register-box.css @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/packages/ui/src/components/ex-register-box/ex-register-box.tsx b/packages/ui/src/components/ex-register-box/ex-register-box.tsx new file mode 100644 index 0000000..db3d551 --- /dev/null +++ b/packages/ui/src/components/ex-register-box/ex-register-box.tsx @@ -0,0 +1,200 @@ +// biome-ignore lint/correctness/noUnusedImports: Stencil requires explicit h factory and decorator imports +import { Component, Event, type EventEmitter, h, State } from "@stencil/core"; +import { ThemeBase } from "../../theme-base"; + +export interface EXRegisterBoxSubmitDetail { + email: string; + password: string; +} + +@Component({ + tag: "ex-register-box", + styleUrl: "ex-register-box.css", + shadow: true, +}) +export class EXRegisterBox extends ThemeBase { + /** Fired when the form is submitted; detail contains email + password. */ + @Event() exSubmit!: EventEmitter; + + /** Fired when the "Sign in" link is clicked. */ + @Event() exSignIn!: EventEmitter; + + @State() private email: string = ""; + @State() private password: string = ""; + @State() private confirmPassword: string = ""; + @State() private showPassword: boolean = false; + @State() private showConfirmPassword: boolean = false; + @State() private confirmError: string = ""; + + private handleSubmit = (e?: Event) => { + e?.preventDefault(); + if (this.password !== this.confirmPassword) { + this.confirmError = "Passwords do not match"; + return; + } + this.confirmError = ""; + this.exSubmit.emit({ email: this.email, password: this.password }); + }; + + render() { + const theme = this.resolvedTheme; + const passwordToggleLabel = this.showPassword ? "Hide" : "Show"; + const confirmToggleLabel = this.showConfirmPassword ? "Hide" : "Show"; + + return ( +
+
+ {/* Header */} +
+

+ Create an account +

+

+ Sign up to get started +

+
+ + {/* Form */} +
+ {/* Email field */} +
+ ) => (this.email = e.detail)} + /> +
+ + {/* Password field */} +
+ ) => (this.password = e.detail)} + onExRightButtonClick={() => (this.showPassword = !this.showPassword)} + /> +
+ + {/* Confirm password field */} +
+ ) => { + onExRightButtonClick={() => (this.showConfirmPassword = !this.showConfirmPassword)} + onExRightButtonClick={() => + (this.showConfirmPassword = !this.showConfirmPassword) + } + /> +
+ +
+ +
+
+ + {/* Social logins slot */} +
+
+
+ + or continue with + +
+
+ +
+ + {/* Sign in link */} +

+ Already have an account?{" "} + +

+
+
+ ); + } +} diff --git a/packages/ui/src/components/ex-register-box/readme.md b/packages/ui/src/components/ex-register-box/readme.md new file mode 100644 index 0000000..1af26fc --- /dev/null +++ b/packages/ui/src/components/ex-register-box/readme.md @@ -0,0 +1,72 @@ +# ex-register-box + + + + + + +## Properties + +| Property | Attribute | Description | Type | Default | +| ------------------------ | -------------------------- | ----------- | --------------------- | ----------- | +| `colorBackground` | `color-background` | | `string \| undefined` | `undefined` | +| `colorDivider` | `color-divider` | | `string \| undefined` | `undefined` | +| `colorError` | `color-error` | | `string \| undefined` | `undefined` | +| `colorInputBackground` | `color-input-background` | | `string \| undefined` | `undefined` | +| `colorInputBorder` | `color-input-border` | | `string \| undefined` | `undefined` | +| `colorInputBorderFocus` | `color-input-border-focus` | | `string \| undefined` | `undefined` | +| `colorLink` | `color-link` | | `string \| undefined` | `undefined` | +| `colorPrimary` | `color-primary` | | `string \| undefined` | `undefined` | +| `colorPrimaryForeground` | `color-primary-foreground` | | `string \| undefined` | `undefined` | +| `colorPrimaryHover` | `color-primary-hover` | | `string \| undefined` | `undefined` | +| `colorSurface` | `color-surface` | | `string \| undefined` | `undefined` | +| `colorText` | `color-text` | | `string \| undefined` | `undefined` | +| `colorTextMuted` | `color-text-muted` | | `string \| undefined` | `undefined` | +| `fontFamily` | `font-family` | | `string \| undefined` | `undefined` | +| `fontSizeLg` | `font-size-lg` | | `string \| undefined` | `undefined` | +| `fontSizeMd` | `font-size-md` | | `string \| undefined` | `undefined` | +| `fontSizeSm` | `font-size-sm` | | `string \| undefined` | `undefined` | +| `fontSizeXl` | `font-size-xl` | | `string \| undefined` | `undefined` | +| `fontSizeXs` | `font-size-xs` | | `string \| undefined` | `undefined` | +| `fontWeightMedium` | `font-weight-medium` | | `string \| undefined` | `undefined` | +| `fontWeightNormal` | `font-weight-normal` | | `string \| undefined` | `undefined` | +| `fontWeightSemibold` | `font-weight-semibold` | | `string \| undefined` | `undefined` | +| `radiusLg` | `radius-lg` | | `string \| undefined` | `undefined` | +| `radiusMd` | `radius-md` | | `string \| undefined` | `undefined` | +| `radiusSm` | `radius-sm` | | `string \| undefined` | `undefined` | +| `radiusXl` | `radius-xl` | | `string \| undefined` | `undefined` | + + +## Events + +| Event | Description | Type | +| ---------- | ------------------------------------------------------------------- | ---------------------------------------- | +| `exSignIn` | Fired when the "Sign in" link is clicked. | `CustomEvent` | +| `exSubmit` | Fired when the form is submitted; detail contains email + password. | `CustomEvent` | + + +## Shadow Parts + +| Part | Description | +| ----------- | ----------- | +| `"sign-in"` | | + + +## Dependencies + +### Depends on + +- [ex-input](../ex-input) +- [ex-button](../ex-button) + +### Graph +```mermaid +graph TD; + ex-register-box --> ex-input + ex-register-box --> ex-button + style ex-register-box fill:#f9f,stroke:#333,stroke-width:4px +``` + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 19283ca..db73755 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -1,6 +1,7 @@ export { EXButton } from "./components/ex-button/ex-button"; export { EXInput } from "./components/ex-input/ex-input"; export { EXLoginBox } from "./components/ex-login-box/ex-login-box"; +export { EXRegisterBox } from "./components/ex-register-box/ex-register-box"; export { EXTestButton } from "./components/ex-test-button/ex-test-button"; export type { EXTheme } from "./theme"; export { DEFAULT_THEME } from "./theme"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d37f89..75ecec8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -700,6 +700,9 @@ importers: packages/ui: devDependencies: + '@expressthat-auth/api-client': + specifier: workspace:* + version: link:../api-client '@expressthat-auth/typescript-config': specifier: workspace:* version: link:../typescript-config From cd096672c19163f2f4ecab1e4c134bf904e8dcdd Mon Sep 17 00:00:00 2001 From: Ross Searle Date: Mon, 27 Apr 2026 05:23:56 +0100 Subject: [PATCH 2/4] fix: Make confirm password input functional in ExRegisterBox The confirm password input's `onExChange` handler was not correctly capturing its value, preventing it from updating the component's state. This commit adds the necessary handler to bind the input's value and removes a redundant `onExRightButtonClick` definition. --- .../ui/src/components/ex-register-box/ex-register-box.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/ui/src/components/ex-register-box/ex-register-box.tsx b/packages/ui/src/components/ex-register-box/ex-register-box.tsx index db3d551..4af0d10 100644 --- a/packages/ui/src/components/ex-register-box/ex-register-box.tsx +++ b/packages/ui/src/components/ex-register-box/ex-register-box.tsx @@ -126,11 +126,8 @@ export class EXRegisterBox extends ThemeBase { right-button-label={confirmToggleLabel} error={this.confirmError} {...theme} - onExChange={(e: CustomEvent) => { - onExRightButtonClick={() => (this.showConfirmPassword = !this.showConfirmPassword)} - onExRightButtonClick={() => - (this.showConfirmPassword = !this.showConfirmPassword) - } + onExChange={(e: CustomEvent) => (this.confirmPassword = e.detail)} + onExRightButtonClick={() => (this.showConfirmPassword = !this.showConfirmPassword)} />
From f49e2611e1350f9baf7698eb9fa30bc67198e467 Mon Sep 17 00:00:00 2001 From: Ross Searle Date: Mon, 27 Apr 2026 06:09:18 +0100 Subject: [PATCH 3/4] feat: add register box, account controller, and dev CORS - Add AccountController with register endpoint - Add ex-register-box Stencil component wired to API - Fix ex-register-box submit button (shadow DOM form workaround) - Allow all CORS in development environment - Add main entry to packages/ui/package.json for Stencil dist - Update stencil.config.ts and web page Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- apps/api/Controllers/AccountController.cs | 20 +++++++++++++++++++ apps/api/Program.cs | 14 +++++++++++++ apps/web/src/app/page.tsx | 7 +------ packages/ui/package.json | 4 +++- .../ex-register-box/ex-register-box.tsx | 18 ++++++++++++++++- packages/ui/stencil.config.ts | 18 +++++++++++++++++ pnpm-lock.yaml | 3 +++ 7 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 apps/api/Controllers/AccountController.cs diff --git a/apps/api/Controllers/AccountController.cs b/apps/api/Controllers/AccountController.cs new file mode 100644 index 0000000..b426bfa --- /dev/null +++ b/apps/api/Controllers/AccountController.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Api.Controllers; + +public record CreateAccountRequest(string Email, string Password); + +public record CreateAccountResponse(int Id, string Email); + + +[ApiController] +[Route("api/[controller]")] +public class AccountController : ControllerBase +{ + [HttpPost] + public ActionResult Register(CreateAccountRequest request) + { + return this.Ok(new CreateAccountResponse(1, request.Email)); + } +} diff --git a/apps/api/Program.cs b/apps/api/Program.cs index 8d4058d..4824690 100644 --- a/apps/api/Program.cs +++ b/apps/api/Program.cs @@ -10,6 +10,15 @@ builder.WebHost.UseUrls($"http://0.0.0.0:{port}"); builder.Services.AddControllers(); + +if (builder.Environment.IsDevelopment()) +{ + builder.Services.AddCors(options => + { + options.AddDefaultPolicy(policy => + policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); + }); +} builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(options => { @@ -61,6 +70,11 @@ [new OpenApiSecuritySchemeReference("Bearer", doc)] = [], c.RoutePrefix = "api/docs"; }); +if (app.Environment.IsDevelopment()) +{ + app.UseCors(); +} + app.MapControllers(); Console.WriteLine($"Starting ExpressThat Auth API on port {port}..., http://localhost:{port}/api/docs"); diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 3b173eb..58a62e7 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -1,7 +1,5 @@ -"use client"; - import { Button } from "@expressthat-auth/internal-components/button"; -import { ExLoginBox, ExRegisterBox, ExTestButton } from "@expressthat-auth/ui-react"; +import { ExLoginBox, ExRegisterBox, ExTestButton } from "@expressthat-auth/ui-react/next"; export default function Home() { return ( @@ -12,9 +10,6 @@ export default function Home() { { - alert("test"); - }} /> diff --git a/packages/ui/package.json b/packages/ui/package.json index a65606d..64e8af8 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -4,6 +4,7 @@ "private": true, "description": "StencilJS web component library for ExpressThat Auth", "type": "module", + "main": "dist/index.cjs.js", "module": "dist/components/index.js", "collection": "dist/collection/collection-manifest.json", "types": "dist/types/index.d.ts", @@ -50,12 +51,13 @@ "check-types": "tsc --noEmit" }, "devDependencies": { - "@expressthat-auth/typescript-config": "workspace:*", "@expressthat-auth/api-client": "workspace:*", + "@expressthat-auth/typescript-config": "workspace:*", "@stencil/angular-output-target": "^0.10.0", "@stencil/core": "^4.30.0", "@stencil/react-output-target": "^1.2.0", "@stencil/vue-output-target": "^0.13.1", + "esbuild": "^0.28.0", "typescript": "6.0.3" } } diff --git a/packages/ui/src/components/ex-register-box/ex-register-box.tsx b/packages/ui/src/components/ex-register-box/ex-register-box.tsx index 4af0d10..aa9e193 100644 --- a/packages/ui/src/components/ex-register-box/ex-register-box.tsx +++ b/packages/ui/src/components/ex-register-box/ex-register-box.tsx @@ -1,6 +1,7 @@ // biome-ignore lint/correctness/noUnusedImports: Stencil requires explicit h factory and decorator imports import { Component, Event, type EventEmitter, h, State } from "@stencil/core"; import { ThemeBase } from "../../theme-base"; +import { createExpressThatAuthClient } from "@expressthat-auth/api-client"; export interface EXRegisterBoxSubmitDetail { email: string; @@ -26,13 +27,28 @@ export class EXRegisterBox extends ThemeBase { @State() private showConfirmPassword: boolean = false; @State() private confirmError: string = ""; + private client = createExpressThatAuthClient("http://localhost:3001"); + + private handleSubmit = (e?: Event) => { + console.log("Submitting registration form with email:", this.email); e?.preventDefault(); if (this.password !== this.confirmPassword) { this.confirmError = "Passwords do not match"; return; } this.confirmError = ""; + + this.client.account.register({ + email: this.email, + password: this.password, + }) .then((response) => { + console.log(`Registration successful: Email: ${response.email}, User ID: ${response.id}`); + }) + .catch((error) => { + console.error("Registration failed:", error); + }); + this.exSubmit.emit({ email: this.email, password: this.password }); }; @@ -132,7 +148,7 @@ export class EXRegisterBox extends ThemeBase {
- +
diff --git a/packages/ui/stencil.config.ts b/packages/ui/stencil.config.ts index 8284043..a891f84 100644 --- a/packages/ui/stencil.config.ts +++ b/packages/ui/stencil.config.ts @@ -1,11 +1,29 @@ import { angularOutputTarget } from "@stencil/angular-output-target"; import type { Config } from "@stencil/core"; +import { transform } from "esbuild"; import { reactOutputTarget } from "@stencil/react-output-target"; import { vueOutputTarget } from "@stencil/vue-output-target"; +import type { Plugin } from "rollup"; + +// Rollup plugin to transpile TypeScript files from workspace packages +// (e.g. @expressthat-auth/api-client uses JIT source exports pointing to .ts files) +const externalTsPlugin: Plugin = { + name: "external-ts", + async transform(code, id) { + if (id.endsWith(".ts") && !id.includes("/ui/src/")) { + const result = await transform(code, { loader: "ts", sourcemap: false }); + return { code: result.code }; + } + return null; + }, +}; export const config: Config = { namespace: "expressthat-auth-ui", minifyJs: false, + rollupPlugins: { + before: [externalTsPlugin], + }, outputTargets: [ { type: "dist", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 75ecec8..35eaa3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -718,6 +718,9 @@ importers: '@stencil/vue-output-target': specifier: ^0.13.1 version: 0.13.1(@stencil/core@4.43.4)(vue-router@5.0.6(@vue/compiler-sfc@3.5.33)(vue@3.5.33(typescript@6.0.3)))(vue@3.5.33(typescript@6.0.3)) + esbuild: + specifier: ^0.28.0 + version: 0.28.0 typescript: specifier: 6.0.3 version: 6.0.3 From 3c5ad1da105b3aea4246ab7eedb785599d47bc92 Mon Sep 17 00:00:00 2001 From: Ross Searle Date: Mon, 27 Apr 2026 06:17:11 +0100 Subject: [PATCH 4/4] style: fix Biome formatting in ex-register-box and stencil.config Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- apps/web/src/app/page.tsx | 5 +-- .../ex-register-box/ex-register-box.tsx | 32 ++++++++++++------- packages/ui/stencil.config.ts | 2 +- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index 58a62e7..2c41c3f 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -7,10 +7,7 @@ export default function Home() {

ExpressThat Auth

Welcome to the authentication portal.

- +

test

diff --git a/packages/ui/src/components/ex-register-box/ex-register-box.tsx b/packages/ui/src/components/ex-register-box/ex-register-box.tsx index aa9e193..b55915a 100644 --- a/packages/ui/src/components/ex-register-box/ex-register-box.tsx +++ b/packages/ui/src/components/ex-register-box/ex-register-box.tsx @@ -1,7 +1,8 @@ // biome-ignore lint/correctness/noUnusedImports: Stencil requires explicit h factory and decorator imports + +import { createExpressThatAuthClient } from "@expressthat-auth/api-client"; import { Component, Event, type EventEmitter, h, State } from "@stencil/core"; import { ThemeBase } from "../../theme-base"; -import { createExpressThatAuthClient } from "@expressthat-auth/api-client"; export interface EXRegisterBoxSubmitDetail { email: string; @@ -29,7 +30,6 @@ export class EXRegisterBox extends ThemeBase { private client = createExpressThatAuthClient("http://localhost:3001"); - private handleSubmit = (e?: Event) => { console.log("Submitting registration form with email:", this.email); e?.preventDefault(); @@ -39,15 +39,17 @@ export class EXRegisterBox extends ThemeBase { } this.confirmError = ""; - this.client.account.register({ - email: this.email, - password: this.password, - }) .then((response) => { - console.log(`Registration successful: Email: ${response.email}, User ID: ${response.id}`); - }) - .catch((error) => { - console.error("Registration failed:", error); - }); + this.client.account + .register({ + email: this.email, + password: this.password, + }) + .then((response) => { + console.log(`Registration successful: Email: ${response.email}, User ID: ${response.id}`); + }) + .catch((error) => { + console.error("Registration failed:", error); + }); this.exSubmit.emit({ email: this.email, password: this.password }); }; @@ -148,7 +150,13 @@ export class EXRegisterBox extends ThemeBase {
- +
diff --git a/packages/ui/stencil.config.ts b/packages/ui/stencil.config.ts index a891f84..417a5d7 100644 --- a/packages/ui/stencil.config.ts +++ b/packages/ui/stencil.config.ts @@ -1,8 +1,8 @@ import { angularOutputTarget } from "@stencil/angular-output-target"; import type { Config } from "@stencil/core"; -import { transform } from "esbuild"; import { reactOutputTarget } from "@stencil/react-output-target"; import { vueOutputTarget } from "@stencil/vue-output-target"; +import { transform } from "esbuild"; import type { Plugin } from "rollup"; // Rollup plugin to transpile TypeScript files from workspace packages