@kahitsan/ksui 0.40.0 → 0.40.2
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kahitsan/ksui",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.2",
|
|
4
4
|
"description": "ksui is a standalone set of SolidJS UI components for KahitSan/Hilinga and any SolidJS app. Published to the public npm registry and consumed as a normal dependency. Ships source under a `solid` export condition so the consumer's vite-plugin-solid compiles it with only solid-js externalized; it depends on nothing but solid-js + lucide-solid and injects its own CSS.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { render } from "@solidjs/testing-library";
|
|
3
|
+
import FormField from "./FormField";
|
|
4
|
+
|
|
5
|
+
describe("FormField", () => {
|
|
6
|
+
it("associates optional id with its label", () => {
|
|
7
|
+
const { getByLabelText } = render(() => (
|
|
8
|
+
<FormField label="Amount" id="amount-field">
|
|
9
|
+
<input id="amount-field" />
|
|
10
|
+
</FormField>
|
|
11
|
+
));
|
|
12
|
+
expect(getByLabelText("Amount").id).toBe("amount-field");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("preserves unlabeled-control compatibility when id is omitted", () => {
|
|
16
|
+
const { getByText } = render(() => (
|
|
17
|
+
<FormField label="Notes">
|
|
18
|
+
<textarea />
|
|
19
|
+
</FormField>
|
|
20
|
+
));
|
|
21
|
+
expect(getByText("Notes")).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
import { Show } from "solid-js";
|
|
7
7
|
import type { JSX } from "solid-js";
|
|
8
8
|
|
|
9
|
-
export default function FormField(props: { label: string; children: JSX.Element; hint?: string }) {
|
|
9
|
+
export default function FormField(props: { label: string; id?: string; children: JSX.Element; hint?: string }) {
|
|
10
10
|
return (
|
|
11
11
|
<div>
|
|
12
|
-
<label class="block text-xs text-[var(--ks-fg-subtle,#71717a)] mb-1">{props.label}</label>
|
|
12
|
+
<label for={props.id} class="block text-xs text-[var(--ks-fg-subtle,#71717a)] mb-1">{props.label}</label>
|
|
13
13
|
{props.children}
|
|
14
14
|
<Show when={props.hint}>
|
|
15
15
|
<p class="text-[10px] text-[var(--ks-fg-subtle,#71717a)] mt-1">{props.hint}</p>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { formatShortDate } from "./formatShortDate";
|
|
3
|
+
|
|
4
|
+
describe("formatShortDate", () => {
|
|
5
|
+
it("preserves date-only values across runtime timezone defaults", () => {
|
|
6
|
+
expect(formatShortDate("2026-09-10")).toBe("Sep 10, 2026");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it("uses the date component from full ISO values", () => {
|
|
10
|
+
expect(formatShortDate("2026-09-10T00:00:00.000Z")).toBe("Sep 10, 2026");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("returns a placeholder for missing or malformed values", () => {
|
|
14
|
+
expect(formatShortDate(null)).toBe("—");
|
|
15
|
+
expect(formatShortDate("not-a-date")).toBe("—");
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
// Short-date formatter for plugin remotes.
|
|
2
2
|
//
|
|
3
3
|
// Renders a YYYY-MM-DD (or full ISO) date string as the en-PH short form
|
|
4
|
-
// (e.g. "Jan 5, 2026").
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// value. Pure helper, no DOM, no fetch.
|
|
8
|
-
|
|
4
|
+
// (e.g. "Jan 5, 2026"). Date-only values are parsed as calendar components
|
|
5
|
+
// so runtime timezone defaults cannot shift their displayed day. Hilinga is an
|
|
6
|
+
// Asia/Manila product, so formatting uses the canonical Manila timezone.
|
|
7
|
+
// Returns an em-dash placeholder for a missing value. Pure helper, no DOM, no fetch.
|
|
9
8
|
export function formatShortDate(dateStr: string | null | undefined): string {
|
|
10
9
|
if (!dateStr) return "—";
|
|
11
10
|
const datePart = dateStr.includes("T") ? dateStr.split("T")[0] : dateStr;
|
|
12
|
-
|
|
11
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(datePart);
|
|
12
|
+
if (!match) return "—";
|
|
13
|
+
const [, year, month, day] = match;
|
|
14
|
+
const date = new Date(Number(year), Number(month) - 1, Number(day));
|
|
15
|
+
return new Intl.DateTimeFormat("en-PH", {
|
|
16
|
+
timeZone: "Asia/Manila",
|
|
13
17
|
year: "numeric",
|
|
14
18
|
month: "short",
|
|
15
19
|
day: "numeric",
|
|
16
|
-
});
|
|
20
|
+
}).format(date);
|
|
17
21
|
}
|