@kouji-ui/core 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,13 @@
1
1
 
2
- > @kouji-ui/core@0.0.2 build /home/runner/work/kouji-ui/kouji-ui/packages/core
2
+ > @kouji-ui/core@0.0.4 build /home/runner/work/kouji-ui/kouji-ui/packages/core
3
3
  > ng-packagr -p ng-package.json
4
4
 
5
5
  Building Angular Package
6
6
 
7
7
  ------------------------------------------------------------------------------
8
+ - Compiling with Angular sources in partial compilation mode.
8
9
  Building entry point '@kouji-ui/core'
9
10
  ------------------------------------------------------------------------------
10
- - Compiling with Angular sources in partial compilation mode.
11
11
  ✔ Compiling with Angular sources in partial compilation mode.
12
12
  ✔ Generating FESM and DTS bundles
13
13
  - Copying assets
@@ -23,5 +23,5 @@ Built Angular Package
23
23
  - to: /home/runner/work/kouji-ui/kouji-ui/dist/kj-core
24
24
  ------------------------------------------------------------------------------
25
25
 
26
- Build at: 2026-05-04T20:37:30.262Z - Time: 4059ms
26
+ Build at: 2026-05-04T22:25:26.490Z - Time: 5957ms
27
27
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @kouji-ui/core
2
2
 
3
+ ## 0.0.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 202d9de: CI fix: husky pre-push hook now skips in CI environments (`$CI` or `$GITHUB_ACTIONS` set). Prevents the Changesets action's automated push from being blocked by the changeset-status gate. No runtime effect on the published package.
8
+ - dc0fe0f: Test-only fix: rewrite `KjToastService` test suite to use `TestBed.inject()` so `inject(KJ_TOAST_STRATEGY)` resolves through Angular DI (was crashing with `new KjToastService()` outside an injection context). No runtime change.
9
+ - d6b40e1: Internal lint cleanup — replace `any` casts in select option storage with typed `HTMLElement & { __kjOptionValue?: unknown }` and convert ternary statement to if/else in overlay toggle. No public API or behavior change.
10
+
11
+ ## 0.0.3
12
+
13
+ ### Patch Changes
14
+
15
+ - c177d1e: Rewrite README with real install/usage docs, primitive index, and design principles. Replaces the Angular CLI scaffold placeholder.
16
+
3
17
  ## 0.0.2
4
18
 
5
19
  ### Patch Changes
package/README.md CHANGED
@@ -1,64 +1,96 @@
1
- # KjCore
1
+ # @kouji-ui/core
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.0.
3
+ Headless Angular 21 UI primitives directives over CDK with WCAG 2.1 AAA semantics and zero CSS.
4
4
 
5
- ## Code scaffolding
5
+ You write the markup. You write the styles. The library wires up keyboard navigation, ARIA, focus management, and state.
6
6
 
7
- Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
7
+ [Documentation](https://kouji-ui.onrender.com) · [GitHub](https://github.com/kouji-dev/kouji-ui)
8
8
 
9
- ```bash
10
- ng generate component component-name
11
- ```
12
-
13
- For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
14
-
15
- ```bash
16
- ng generate --help
17
- ```
18
-
19
- ## Building
20
-
21
- To build the library, run:
9
+ ## Install
22
10
 
23
11
  ```bash
24
- ng build kj-core
12
+ pnpm add @kouji-ui/core @angular/cdk
25
13
  ```
26
14
 
27
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
15
+ Peer dependencies: `@angular/common` `@angular/core` `@angular/cdk` all `^21.0.0`.
28
16
 
29
- ### Publishing the Library
17
+ ## Quick start
30
18
 
31
- Once the project is built, you can publish your library by following these steps:
19
+ Every primitive is a standalone directive. Import it where you use it.
32
20
 
33
- 1. Navigate to the `dist` directory:
21
+ ```ts
22
+ import { Component } from '@angular/core';
23
+ import { KjButton } from '@kouji-ui/core';
34
24
 
35
- ```bash
36
- cd dist/kj-core
37
- ```
25
+ @Component({
26
+ selector: 'app-example',
27
+ imports: [KjButton],
28
+ template: `<button kjButton (click)="save()">Save</button>`,
29
+ })
30
+ export class ExampleComponent {
31
+ save() { /* ... */ }
32
+ }
33
+ ```
38
34
 
39
- 2. Run the `npm publish` command to publish your library to the npm registry:
40
- ```bash
41
- npm publish
42
- ```
35
+ The directive owns the keyboard contract, ARIA roles, and focus ring not the look. Style the host element however you want.
36
+
37
+ ### A more involved example: dialog
38
+
39
+ ```ts
40
+ import { Component } from '@angular/core';
41
+ import {
42
+ KjDialog,
43
+ KjDialogTrigger,
44
+ KjDialogOverlay,
45
+ KjDialogTitle,
46
+ KjDialogClose,
47
+ } from '@kouji-ui/core';
48
+
49
+ @Component({
50
+ selector: 'app-confirm',
51
+ imports: [KjDialog, KjDialogTrigger, KjDialogOverlay, KjDialogTitle, KjDialogClose],
52
+ template: `
53
+ <button [kjDialogTrigger]="confirm">Delete</button>
54
+
55
+ <ng-template #confirm>
56
+ <div kjDialog>
57
+ <div kjDialogOverlay></div>
58
+ <h2 kjDialogTitle>Delete this item?</h2>
59
+ <p>This action cannot be undone.</p>
60
+ <button kjDialogClose>Cancel</button>
61
+ <button kjDialogClose (click)="delete()">Delete</button>
62
+ </div>
63
+ </ng-template>
64
+ `,
65
+ })
66
+ export class ConfirmComponent {
67
+ delete() { /* ... */ }
68
+ }
69
+ ```
43
70
 
44
- ## Running unit tests
71
+ Focus trap, Escape-to-close, scroll lock, ARIA wiring — all handled.
45
72
 
46
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
73
+ ## What's included
47
74
 
48
- ```bash
49
- ng test
50
- ```
75
+ | Domain | Primitives |
76
+ | --------------- | ----------------------------------------------------------- |
77
+ | Buttons & inputs | `KjButton`, `KjInput`, `KjCheckbox`, `KjRadio`, `KjToggle`, `KjSelect` |
78
+ | Form composition | `KjFormField`, `KjFormLabel`, `KjFormError` |
79
+ | Overlays | `KjDialog`, `KjPopover`, `KjTooltip`, `KjToast`, `KjMenu` |
80
+ | Navigation | `KjTabs`, `KjAccordion` |
81
+ | Data display | `KjTable`, `KjBadge`, `KjAvatar`, `KjChart` |
82
+ | A11y primitives | `KjFocusTrap`, `KjFocusRing`, `KjLiveRegion`, `KjRovingTabindex`, `KjVisuallyHidden`, `KjAriaDescribedBy` |
51
83
 
52
- ## Running end-to-end tests
84
+ Every primitive ships with examples and an inputs reference at [kouji-ui.onrender.com](https://kouji-ui.onrender.com).
53
85
 
54
- For end-to-end (e2e) testing, run:
55
-
56
- ```bash
57
- ng e2e
58
- ```
86
+ ## Design principles
59
87
 
60
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
88
+ - **Headless** directives, not components. You own the DOM and CSS.
89
+ - **WCAG 2.1 AAA target** — keyboard contracts, focus management, ARIA, and contrast all designed for AAA.
90
+ - **CDK-aligned** — wraps `@angular/cdk` primitives where it makes sense (overlay, focus-trap), never reimplements from scratch.
91
+ - **Signals-first** — public APIs use `input()` / `signal()` / `effect()`.
92
+ - **Zero runtime CSS** — no shipped stylesheet. Bring your own design system.
61
93
 
62
- ## Additional Resources
94
+ ## License
63
95
 
64
- For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
96
+ MIT © [kouji-dev](https://github.com/kouji-dev)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kouji-ui/core",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Headless Angular 21 UI primitives — directives over CDK with WCAG 2.1 AAA semantics and zero CSS.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,4 +1,4 @@
1
- import { render, fireEvent } from '@testing-library/angular';
1
+ import { render } from '@testing-library/angular';
2
2
  import { axe, toHaveNoViolations } from 'jest-axe';
3
3
  import { KjButton } from './button';
4
4
 
@@ -75,7 +75,7 @@ describe('KjDialogTrigger', () => {
75
75
  });
76
76
 
77
77
  it('closes dialog on overlay click (close on backdrop enabled by default)', async () => {
78
- const { getByRole, getByText } = await render(
78
+ const { getByRole } = await render(
79
79
  `<button [kjDialogTrigger]="dlg">Open</button>
80
80
  <ng-template #dlg>
81
81
  <div kjDialogOverlay>
@@ -1,7 +1,7 @@
1
1
  import { Component } from '@angular/core';
2
2
  import { FormControl, ReactiveFormsModule } from '@angular/forms';
3
- import { render, fireEvent } from '@testing-library/angular';
4
- import { axe, toHaveNoViolations } from 'jest-axe';
3
+ import { render } from '@testing-library/angular';
4
+ import { toHaveNoViolations } from 'jest-axe';
5
5
  import { KjFormControl } from './form-control';
6
6
 
7
7
  expect.extend(toHaveNoViolations);
@@ -14,7 +14,7 @@ expect.extend(toHaveNoViolations);
14
14
  class TestHostComponent {
15
15
  ctrl = new FormControl('');
16
16
  formCtrl!: KjFormControl;
17
- onInput(e: Event): void {
17
+ onInput(_e: Event): void {
18
18
  // will be wired by the directive consumer pattern
19
19
  }
20
20
  onBlur(): void {}
@@ -1,4 +1,4 @@
1
- import { Component, TemplateRef, ViewChild, ViewContainerRef, inject } from '@angular/core';
1
+ import { Component, TemplateRef, ViewChild, ViewContainerRef, inject, OnInit } from '@angular/core';
2
2
  import { render } from '@testing-library/angular';
3
3
  import { KjOverlayService, KjOverlayRef } from './overlay';
4
4
 
@@ -17,7 +17,7 @@ describe('KjOverlayService', () => {
17
17
  standalone: true,
18
18
  template: '<ng-template #tpl><div>overlay content</div></ng-template>',
19
19
  })
20
- class TestComponent {
20
+ class TestComponent implements OnInit {
21
21
  @ViewChild('tpl', { static: true }) tpl!: TemplateRef<unknown>;
22
22
  readonly svc = inject(KjOverlayService);
23
23
  readonly vcr = inject(ViewContainerRef);
@@ -34,7 +34,7 @@ describe('KjOverlayService', () => {
34
34
  standalone: true,
35
35
  template: '<ng-template #tpl><div>overlay</div></ng-template>',
36
36
  })
37
- class TestComponent {
37
+ class TestComponent implements OnInit {
38
38
  @ViewChild('tpl', { static: true }) tpl!: TemplateRef<unknown>;
39
39
  readonly svc = inject(KjOverlayService);
40
40
  readonly vcr = inject(ViewContainerRef);
@@ -53,7 +53,7 @@ describe('KjOverlayService', () => {
53
53
  standalone: true,
54
54
  template: '<ng-template #tpl><div>overlay</div></ng-template>',
55
55
  })
56
- class TestComponent {
56
+ class TestComponent implements OnInit {
57
57
  @ViewChild('tpl', { static: true }) tpl!: TemplateRef<unknown>;
58
58
  readonly svc = inject(KjOverlayService);
59
59
  readonly vcr = inject(ViewContainerRef);
@@ -73,7 +73,7 @@ describe('KjOverlayService', () => {
73
73
  standalone: true,
74
74
  template: '<ng-template #tpl><div>overlay</div></ng-template>',
75
75
  })
76
- class TestComponent {
76
+ class TestComponent implements OnInit {
77
77
  @ViewChild('tpl', { static: true }) tpl!: TemplateRef<unknown>;
78
78
  readonly svc = inject(KjOverlayService);
79
79
  readonly vcr = inject(ViewContainerRef);
@@ -41,7 +41,11 @@ export class KjOverlayRef {
41
41
 
42
42
  /** Toggles the overlay between open and closed states. */
43
43
  toggle(): void {
44
- this.isOpen() ? this.close() : this.open();
44
+ if (this.isOpen()) {
45
+ this.close();
46
+ } else {
47
+ this.open();
48
+ }
45
49
  }
46
50
 
47
51
  /**
@@ -188,7 +188,7 @@ export class KjSelectContent {
188
188
  this.el.nativeElement.querySelectorAll('[kjOption]')
189
189
  ) as HTMLElement[]).map((el, i) => ({
190
190
  el,
191
- value: (el as any).__kjOptionValue,
191
+ value: (el as HTMLElement & { __kjOptionValue?: unknown }).__kjOptionValue,
192
192
  id: el.id || `kj-option-${i}`,
193
193
  }));
194
194
  }
@@ -234,7 +234,7 @@ export class KjOption {
234
234
 
235
235
  constructor() {
236
236
  effect(() => {
237
- (this.el.nativeElement as any).__kjOptionValue = this.kjOptionValue();
237
+ (this.el.nativeElement as HTMLElement & { __kjOptionValue?: unknown }).__kjOptionValue = this.kjOptionValue();
238
238
  });
239
239
  afterNextRender(() => {
240
240
  if (!this.el.nativeElement.id) {
@@ -1,6 +1,6 @@
1
1
  import { Component } from '@angular/core';
2
2
  import { ColumnDef } from '@tanstack/angular-table';
3
- import { render, fireEvent } from '@testing-library/angular';
3
+ import { render } from '@testing-library/angular';
4
4
  import { axe, toHaveNoViolations } from 'jest-axe';
5
5
  import { KjTable, KjTableHeader } from './table';
6
6
 
@@ -1,8 +1,8 @@
1
- import { render, fireEvent } from '@testing-library/angular';
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { render } from '@testing-library/angular';
2
3
  import { axe, toHaveNoViolations } from 'jest-axe';
3
4
  import { KjToast, KjToastViewport } from './toast';
4
5
  import { KjToastService } from './toast.service';
5
- import { Component, inject } from '@angular/core';
6
6
 
7
7
  expect.extend(toHaveNoViolations);
8
8
 
@@ -47,43 +47,44 @@ describe('KjToastViewport', () => {
47
47
  });
48
48
 
49
49
  describe('KjToastService', () => {
50
- it('show(message) adds a toast', async () => {
51
- const svc = new KjToastService();
50
+ let svc: KjToastService;
51
+
52
+ beforeEach(() => {
53
+ TestBed.configureTestingModule({});
54
+ svc = TestBed.inject(KjToastService);
55
+ });
56
+
57
+ it('show(message) adds a toast', () => {
52
58
  expect(svc.toasts().length).toBe(0);
53
59
  svc.show('Hello', { duration: 0 });
54
60
  expect(svc.toasts().length).toBe(1);
55
61
  expect(svc.toasts()[0].message).toBe('Hello');
56
62
  });
57
63
 
58
- it('dismiss() removes a toast', async () => {
59
- const svc = new KjToastService();
64
+ it('dismiss() removes a toast', () => {
60
65
  const id = svc.show('Test', { duration: 0 });
61
66
  svc.dismiss(id);
62
67
  expect(svc.toasts().length).toBe(0);
63
68
  });
64
69
 
65
- it('success() sets variant=success', async () => {
66
- const svc = new KjToastService();
70
+ it('success() sets variant=success', () => {
67
71
  svc.success('Done', { duration: 0 });
68
72
  expect(svc.toasts()[0].variant).toBe('success');
69
73
  });
70
74
 
71
- it('error() sets variant=destructive', async () => {
72
- const svc = new KjToastService();
75
+ it('error() sets variant=destructive', () => {
73
76
  svc.error('Fail', { duration: 0 });
74
77
  expect(svc.toasts()[0].variant).toBe('destructive');
75
78
  });
76
79
 
77
- it('dismissAll() clears all toasts', async () => {
78
- const svc = new KjToastService();
80
+ it('dismissAll() clears all toasts', () => {
79
81
  svc.show('A', { duration: 0 });
80
82
  svc.show('B', { duration: 0 });
81
83
  svc.dismissAll();
82
84
  expect(svc.toasts().length).toBe(0);
83
85
  });
84
86
 
85
- it('contextFor() exposes a bound dismiss callback', async () => {
86
- const svc = new KjToastService();
87
+ it('contextFor() exposes a bound dismiss callback', () => {
87
88
  const id = svc.show('Hello', { duration: 0 });
88
89
  const ctx = svc.contextFor(svc.toasts()[0]);
89
90
  expect(ctx.id).toBe(id);
@@ -2,7 +2,7 @@ import {
2
2
  DestroyRef, Directive, ElementRef, TemplateRef,
3
3
  afterNextRender, computed, inject, input, signal,
4
4
  } from '@angular/core';
5
- import { KjToastContext, KjToastService, KjToastTemplateContext, KjToastVariant } from './toast.service';
5
+ import { KjToastService, KjToastTemplateContext, KjToastVariant } from './toast.service';
6
6
  import { KJ_TOAST_STRATEGY } from './toast.strategy';
7
7
  import { KjToastPositionX, KjToastPositionY } from './toast.types';
8
8