@astryxdesign/cli 0.1.2-canary.b712653 → 0.1.2-canary.bbf77ea
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/CHANGELOG.md +0 -1
- package/README.md +49 -55
- package/docs/theme.doc.mjs +2 -2
- package/package.json +19 -7
- package/src/api/component.mjs +253 -12
- package/src/api/discover.mjs +14 -7
- package/src/api/doctor.mjs +10 -25
- package/src/api/layout.mjs +11 -10
- package/src/api/layout.test.mjs +4 -1
- package/src/api/template-integration.test.mjs +225 -0
- package/src/api/template.mjs +242 -84
- package/src/api/validate-integration.mjs +370 -0
- package/src/api/validate-integration.test.mjs +222 -0
- package/src/codemod.mjs +93 -0
- package/src/codemod.test.mjs +134 -0
- package/src/codemods/__tests__/registry.test.mjs +1 -0
- package/src/codemods/__tests__/runner.test.mjs +103 -0
- package/src/codemods/integration-discovery.mjs +168 -0
- package/src/codemods/integration-discovery.test.mjs +234 -0
- package/src/codemods/integration-runner.mjs +109 -0
- package/src/codemods/registry.mjs +1 -0
- package/src/codemods/run-codemod.mjs +207 -0
- package/src/codemods/runner.mjs +73 -96
- package/src/codemods/transforms/v0.0.15/index.mjs +0 -13
- package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-css-surfaces.test.mjs +67 -0
- package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-declare-module.test.mjs +61 -0
- package/src/codemods/transforms/v0.1.0/__tests__/v0.1.0-ordering.test.mjs +104 -0
- package/src/codemods/transforms/{v0.0.15 → v0.1.0}/drop-xds-prefix-imports.mjs +15 -4
- package/src/codemods/transforms/v0.1.0/index.mjs +43 -6
- package/src/codemods/transforms/v0.1.0/migrate-xds-css-surfaces.mjs +77 -0
- package/src/codemods/transforms/v0.1.0/migrate-xds-declare-module.mjs +78 -0
- package/src/codemods/transforms/v0.1.3/__tests__/migrate-layout-components-to-experimental.test.mjs +360 -0
- package/src/codemods/transforms/v0.1.3/index.mjs +19 -0
- package/src/codemods/transforms/v0.1.3/migrate-layout-components-to-experimental.mjs +260 -0
- package/src/commands/agent-docs.mjs +1 -1
- package/src/commands/component/index.mjs +44 -10
- package/src/commands/component-ownership.test.mjs +227 -0
- package/src/commands/discover.mjs +5 -16
- package/src/commands/doctor.test.mjs +3 -3
- package/src/commands/json-contract.test.mjs +0 -32
- package/src/commands/swizzle.mjs +224 -235
- package/src/commands/swizzle.path-safety.test.mjs +3 -3
- package/src/commands/swizzle.routing.test.mjs +279 -0
- package/src/commands/template.mjs +33 -37
- package/src/commands/upgrade.config-ordering.test.mjs +273 -0
- package/src/commands/upgrade.integration-policy.test.mjs +204 -0
- package/src/commands/upgrade.mjs +349 -170
- package/src/commands/validate-integration.mjs +110 -0
- package/src/commands/validate-integration.test.mjs +124 -0
- package/src/config.mjs +7 -20
- package/src/config.test.mjs +89 -13
- package/src/index.mjs +7 -3
- package/src/integration.mjs +19 -0
- package/src/lib/component-discovery.mjs +177 -0
- package/src/lib/config-cache.mjs +91 -0
- package/src/lib/config-cache.test.mjs +83 -0
- package/src/lib/config-schema.mjs +41 -55
- package/src/lib/error-codes.mjs +7 -12
- package/src/lib/integration-warnings.mjs +62 -0
- package/src/lib/integration-warnings.test.mjs +102 -0
- package/src/lib/integrations.mjs +92 -115
- package/src/lib/integrations.test.mjs +88 -107
- package/src/lib/manifest.mjs +5 -2
- package/src/lib/module-loader.mjs +80 -0
- package/src/lib/module-loader.test.mjs +106 -0
- package/src/lib/project.mjs +502 -0
- package/src/lib/project.test.mjs +308 -0
- package/src/template.mjs +73 -0
- package/src/template.test.mjs +127 -0
- package/src/types/api.d.ts +2 -6
- package/src/types/base.d.ts +3 -9
- package/src/types/codemod.d.ts +81 -0
- package/src/types/component.d.ts +27 -2
- package/src/types/config.d.ts +56 -85
- package/src/types/error-codes.d.ts +5 -3
- package/src/types/index.d.ts +0 -1
- package/src/types/integration.d.ts +29 -0
- package/src/types/swizzle.d.ts +9 -2
- package/src/types/template-api.d.ts +54 -0
- package/src/types/template.d.ts +10 -7
- package/src/types/upgrade.d.ts +29 -0
- package/src/types/validate-integration.d.ts +24 -0
- package/src/utils/github.mjs +0 -237
- package/src/utils/interactive.mjs +2 -2
- package/templates/blocks/components/DateRangeInput/DateRangeInputWithPresets.doc.mjs +14 -0
- package/templates/blocks/components/DateRangeInput/DateRangeInputWithPresets.tsx +46 -0
- package/templates/blocks/components/DateRangeInput/DateRangeInputWithValidation.doc.mjs +14 -0
- package/templates/blocks/components/DateRangeInput/DateRangeInputWithValidation.tsx +52 -0
- package/templates/blocks/components/DateTimeInput/DateTimeInputWithValidation.doc.mjs +14 -0
- package/templates/blocks/components/DateTimeInput/DateTimeInputWithValidation.tsx +43 -0
- package/templates/blocks/components/DropdownMenu/DropdownMenuShowcase.tsx +0 -1
- package/templates/blocks/components/DropdownMenuItem/DropdownMenuItemShowcase.tsx +0 -1
- package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.doc.mjs +1 -1
- package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.tsx +9 -4
- package/templates/blocks/components/LinkProvider/LinkProviderCustomLink.doc.mjs +1 -1
- package/templates/blocks/components/MoreMenu/MoreMenuShowcase.tsx +0 -1
- package/templates/blocks/components/Tab/TabWithSelectedIcon.doc.mjs +13 -0
- package/templates/blocks/components/Tab/TabWithSelectedIcon.tsx +39 -0
- package/templates/blocks/components/Table/StickyColumnsHookUsage.doc.mjs +1 -1
- package/templates/pages/shell-side-nav/page.tsx +0 -1
- package/templates/pages/shell-side-nav/template.doc.mjs +1 -1
- package/templates/pages/shell-top-nav/template.doc.mjs +1 -1
- package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-config-surfaces.test.mjs +0 -116
- package/src/codemods/transforms/v0.1.0/migrate-xds-config-surfaces.mjs +0 -230
- package/src/commands/gap-report.mjs +0 -464
- package/src/commands/gap-report.test.mjs +0 -168
- package/src/commands/swizzle-gap-safety.test.mjs +0 -273
- package/src/lib/config.mjs +0 -113
- package/src/lib/config.test.mjs +0 -91
- package/src/types/gap-report.d.ts +0 -29
- /package/src/codemods/transforms/{v0.0.15 → v0.1.0}/__tests__/drop-xds-prefix-imports.test.mjs +0 -0
package/src/utils/github.mjs
CHANGED
|
@@ -4,69 +4,9 @@
|
|
|
4
4
|
* @file GitHub utilities for Astryx CLI
|
|
5
5
|
*
|
|
6
6
|
* Shared helpers for interacting with the GitHub CLI (`gh`).
|
|
7
|
-
* Used by gap-report and swizzle commands.
|
|
8
|
-
*
|
|
9
|
-
* Gap reporting can be configured via astryx.config.mjs:
|
|
10
|
-
* gapReport: false — disable entirely
|
|
11
|
-
* gapReport: { command: './scripts/report-gap.sh' } — run custom script (receives JSON on stdin)
|
|
12
|
-
*
|
|
13
|
-
* Or via environment variable:
|
|
14
|
-
* ASTRYX_GAP_REPORT=off — disable entirely
|
|
15
|
-
* ASTRYX_GAP_REPORT=./scripts/report-gap.sh — run custom script
|
|
16
7
|
*/
|
|
17
8
|
|
|
18
9
|
import {execFileSync} from 'node:child_process';
|
|
19
|
-
import * as path from 'node:path';
|
|
20
|
-
import {loadConfig} from '../lib/config.mjs';
|
|
21
|
-
|
|
22
|
-
/** Default target repository for gap report issues. */
|
|
23
|
-
const DEFAULT_REPO = 'facebookexperimental/xds';
|
|
24
|
-
|
|
25
|
-
/** Gap report category options. */
|
|
26
|
-
export const GAP_CATEGORIES = [
|
|
27
|
-
{value: 'missing_component', label: 'Missing component'},
|
|
28
|
-
{value: 'missing_variant', label: 'Missing variant or prop'},
|
|
29
|
-
{value: 'layout_gap', label: 'Layout gap'},
|
|
30
|
-
{value: 'styling_gap', label: 'Styling gap'},
|
|
31
|
-
{value: 'a11y_gap', label: 'Accessibility gap'},
|
|
32
|
-
{value: 'api_friction', label: 'API friction'},
|
|
33
|
-
{value: 'docs_gap', label: 'Documentation gap'},
|
|
34
|
-
{value: 'other', label: 'Other'},
|
|
35
|
-
];
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Load gap report configuration.
|
|
39
|
-
*
|
|
40
|
-
* Priority: env var > astryx.config.mjs > default (GitHub issue)
|
|
41
|
-
*
|
|
42
|
-
* Returns { enabled: boolean, command?: string }
|
|
43
|
-
* - enabled: false → gap reporting disabled
|
|
44
|
-
* - command: string → run this script instead of creating a GitHub issue
|
|
45
|
-
* - neither → default behavior (GitHub issue to facebookexperimental/xds)
|
|
46
|
-
*/
|
|
47
|
-
export async function loadGapReportConfig() {
|
|
48
|
-
// 1. Check environment variable
|
|
49
|
-
const envVar = process.env.ASTRYX_GAP_REPORT;
|
|
50
|
-
if (envVar) {
|
|
51
|
-
if (envVar === 'off' || envVar === 'false' || envVar === '0') {
|
|
52
|
-
return {enabled: false};
|
|
53
|
-
}
|
|
54
|
-
// Treat as custom command
|
|
55
|
-
return {enabled: true, command: envVar};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// 2. Check astryx.config.mjs + integration manifests
|
|
59
|
-
const config = await loadConfig(process.cwd());
|
|
60
|
-
if (config.gapReport !== undefined) {
|
|
61
|
-
if (config.gapReport === false) return {enabled: false};
|
|
62
|
-
if (config.gapReport?.command) {
|
|
63
|
-
return {enabled: true, command: config.gapReport.command};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// 3. Default
|
|
68
|
-
return {enabled: true};
|
|
69
|
-
}
|
|
70
10
|
|
|
71
11
|
/**
|
|
72
12
|
* Check if `gh` CLI is installed and authenticated.
|
|
@@ -80,180 +20,3 @@ export function checkGhCli() {
|
|
|
80
20
|
return false;
|
|
81
21
|
}
|
|
82
22
|
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Idempotently create the `gap-report` and `gap-analysis` labels.
|
|
86
|
-
* Uses `--force` to avoid errors if labels already exist.
|
|
87
|
-
*/
|
|
88
|
-
function ensureGapReportLabel() {
|
|
89
|
-
try {
|
|
90
|
-
execFileSync(
|
|
91
|
-
'gh',
|
|
92
|
-
[
|
|
93
|
-
'label',
|
|
94
|
-
'create',
|
|
95
|
-
'gap-report',
|
|
96
|
-
'--repo',
|
|
97
|
-
DEFAULT_REPO,
|
|
98
|
-
'--color',
|
|
99
|
-
'D4C5F9',
|
|
100
|
-
'--description',
|
|
101
|
-
'Auto-filed gap report from swizzle/gap-report CLI',
|
|
102
|
-
'--force',
|
|
103
|
-
],
|
|
104
|
-
{stdio: 'ignore'},
|
|
105
|
-
);
|
|
106
|
-
execFileSync(
|
|
107
|
-
'gh',
|
|
108
|
-
[
|
|
109
|
-
'label',
|
|
110
|
-
'create',
|
|
111
|
-
'gap-analysis',
|
|
112
|
-
'--repo',
|
|
113
|
-
DEFAULT_REPO,
|
|
114
|
-
'--color',
|
|
115
|
-
'0E8A16',
|
|
116
|
-
'--description',
|
|
117
|
-
'Curated gap analysis summary',
|
|
118
|
-
'--force',
|
|
119
|
-
],
|
|
120
|
-
{stdio: 'ignore'},
|
|
121
|
-
);
|
|
122
|
-
} catch {
|
|
123
|
-
// Best-effort — labels may already exist or user may lack permissions
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Run a custom gap report command, piping report data as JSON to stdin.
|
|
129
|
-
*
|
|
130
|
-
* @param {string} command Script path to execute
|
|
131
|
-
* @param {object} report Report data (component, category, intention, detail, source)
|
|
132
|
-
* @returns {string} stdout from the command
|
|
133
|
-
*/
|
|
134
|
-
function runCustomCommand(command, report) {
|
|
135
|
-
const resolved = path.resolve(process.cwd(), command);
|
|
136
|
-
const json = JSON.stringify(report, null, 2);
|
|
137
|
-
|
|
138
|
-
const result = execFileSync(resolved, [], {
|
|
139
|
-
input: json,
|
|
140
|
-
encoding: 'utf-8',
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
return result.trim();
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Build the report payload + rendered title/body for a gap report.
|
|
148
|
-
* Pure function — no side effects, never calls `gh` or external scripts.
|
|
149
|
-
*
|
|
150
|
-
* @returns {{
|
|
151
|
-
* enabled: boolean,
|
|
152
|
-
* mode: 'disabled' | 'custom' | 'github',
|
|
153
|
-
* command?: string,
|
|
154
|
-
* report: object,
|
|
155
|
-
* title: string,
|
|
156
|
-
* body: string,
|
|
157
|
-
* repo: string,
|
|
158
|
-
* }}
|
|
159
|
-
*/
|
|
160
|
-
export async function buildGapReportPreview({
|
|
161
|
-
component,
|
|
162
|
-
category,
|
|
163
|
-
intention,
|
|
164
|
-
detail,
|
|
165
|
-
source = 'cli',
|
|
166
|
-
}) {
|
|
167
|
-
const config = await loadGapReportConfig();
|
|
168
|
-
const categoryLabel =
|
|
169
|
-
GAP_CATEGORIES.find(c => c.value === category)?.label ?? category;
|
|
170
|
-
|
|
171
|
-
const report = {
|
|
172
|
-
component,
|
|
173
|
-
category,
|
|
174
|
-
categoryLabel,
|
|
175
|
-
intention,
|
|
176
|
-
detail: detail || null,
|
|
177
|
-
source,
|
|
178
|
-
timestamp: new Date().toISOString(),
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
const title = `[gap] ${component}: ${intention.slice(0, 70)}`;
|
|
182
|
-
const body = [
|
|
183
|
-
'| Field | Value |',
|
|
184
|
-
'|-------|-------|',
|
|
185
|
-
`| **Component** | ${component} |`,
|
|
186
|
-
`| **Category** | ${categoryLabel} |`,
|
|
187
|
-
`| **Source** | ${source} |`,
|
|
188
|
-
`| **Timestamp** | ${report.timestamp} |`,
|
|
189
|
-
'',
|
|
190
|
-
'## User Intention',
|
|
191
|
-
'',
|
|
192
|
-
intention,
|
|
193
|
-
...(detail ? ['', '## Additional Context', '', detail] : []),
|
|
194
|
-
].join('\n');
|
|
195
|
-
|
|
196
|
-
let mode = 'github';
|
|
197
|
-
if (!config.enabled) mode = 'disabled';
|
|
198
|
-
else if (config.command) mode = 'custom';
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
enabled: config.enabled,
|
|
202
|
-
mode,
|
|
203
|
-
command: config.command,
|
|
204
|
-
report,
|
|
205
|
-
title,
|
|
206
|
-
body,
|
|
207
|
-
repo: DEFAULT_REPO,
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Create a gap report — either via GitHub issue or custom command.
|
|
213
|
-
*
|
|
214
|
-
* SAFETY: This actually invokes `gh issue create` (or runs the custom command).
|
|
215
|
-
* Callers MUST gate this behind explicit user confirmation or a `--commit`-style
|
|
216
|
-
* flag in non-interactive contexts. Use `buildGapReportPreview` for dry-run.
|
|
217
|
-
*
|
|
218
|
-
* @param {object} opts
|
|
219
|
-
* @param {string} opts.component Component name (e.g. "Button")
|
|
220
|
-
* @param {string} opts.category One of GAP_CATEGORIES values
|
|
221
|
-
* @param {string} opts.intention What the user was trying to achieve
|
|
222
|
-
* @param {string} [opts.detail] Additional context
|
|
223
|
-
* @param {string} [opts.source] Who filed it: "interactive", "llm-auto", "cli"
|
|
224
|
-
* @returns {string|null} URL of the created issue (or custom command output), null if disabled
|
|
225
|
-
*/
|
|
226
|
-
export async function createGapReport(opts) {
|
|
227
|
-
const preview = await buildGapReportPreview(opts);
|
|
228
|
-
|
|
229
|
-
if (!preview.enabled) {
|
|
230
|
-
return null;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Custom command mode — pipe JSON to the script
|
|
234
|
-
if (preview.mode === 'custom') {
|
|
235
|
-
return runCustomCommand(preview.command, preview.report);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Default mode — create GitHub issue
|
|
239
|
-
ensureGapReportLabel();
|
|
240
|
-
|
|
241
|
-
const result = execFileSync(
|
|
242
|
-
'gh',
|
|
243
|
-
[
|
|
244
|
-
'issue',
|
|
245
|
-
'create',
|
|
246
|
-
'--repo',
|
|
247
|
-
preview.repo,
|
|
248
|
-
'--title',
|
|
249
|
-
preview.title,
|
|
250
|
-
'--body',
|
|
251
|
-
preview.body,
|
|
252
|
-
'--label',
|
|
253
|
-
'gap-report',
|
|
254
|
-
],
|
|
255
|
-
{encoding: 'utf-8'},
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
return result.trim();
|
|
259
|
-
}
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* fail fast (exit 1) with actionable, non-interactive guidance.
|
|
19
19
|
*
|
|
20
20
|
* - `isInteractive()` — for commands with an OPTIONAL secondary prompt
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* that runs after the primary work has already succeeded; callers use
|
|
22
|
+
* this to skip the prompt gracefully in non-interactive contexts.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
exampleFor: 'DateRangeInput',
|
|
7
|
+
name: 'DateRangeInput — With Presets',
|
|
8
|
+
displayName: 'DateRangeInput — With Presets',
|
|
9
|
+
description:
|
|
10
|
+
'Date range picker with quick-select presets for common periods. Use for analytics dashboards, report filters, or any context where users frequently select standard time windows.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: ['DateRangeInput', 'Layout', 'Text'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import {DateRangeInput} from '@astryxdesign/core/DateRangeInput';
|
|
7
|
+
import type {DateRange} from '@astryxdesign/core/DateRangeInput';
|
|
8
|
+
import type {ISODateString} from '@astryxdesign/core/Calendar';
|
|
9
|
+
import {Stack} from '@astryxdesign/core/Layout';
|
|
10
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
11
|
+
|
|
12
|
+
function daysAgo(n: number): ISODateString {
|
|
13
|
+
const d = new Date();
|
|
14
|
+
d.setDate(d.getDate() - n);
|
|
15
|
+
return d.toISOString().slice(0, 10) as ISODateString;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function today(): ISODateString {
|
|
19
|
+
return new Date().toISOString().slice(0, 10) as ISODateString;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const presets = [
|
|
23
|
+
{label: 'Last 7 days', getRange: () => ({start: daysAgo(7), end: today()})},
|
|
24
|
+
{label: 'Last 14 days', getRange: () => ({start: daysAgo(14), end: today()})},
|
|
25
|
+
{label: 'Last 30 days', getRange: () => ({start: daysAgo(30), end: today()})},
|
|
26
|
+
{label: 'Last 90 days', getRange: () => ({start: daysAgo(90), end: today()})},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export default function DateRangeInputWithPresets() {
|
|
30
|
+
const [range, setRange] = useState<DateRange | null>(null);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Stack direction="vertical" gap={4} width="100%" style={{maxWidth: 400}}>
|
|
34
|
+
<Text type="supporting" color="secondary">
|
|
35
|
+
{range ? `${range.start} → ${range.end}` : 'No range selected'}
|
|
36
|
+
</Text>
|
|
37
|
+
<DateRangeInput
|
|
38
|
+
label="Report period"
|
|
39
|
+
description="Use a preset or pick a custom range"
|
|
40
|
+
value={range}
|
|
41
|
+
onChange={setRange}
|
|
42
|
+
presets={presets}
|
|
43
|
+
/>
|
|
44
|
+
</Stack>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
exampleFor: 'DateRangeInput',
|
|
7
|
+
name: 'DateRangeInput — Validation',
|
|
8
|
+
displayName: 'DateRangeInput — Validation',
|
|
9
|
+
description:
|
|
10
|
+
'Date range input in all three status states: error, warning, and success. Use to surface booking conflicts, flag high-demand periods, or confirm an available range.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: ['DateRangeInput', 'Layout'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import {DateRangeInput} from '@astryxdesign/core/DateRangeInput';
|
|
7
|
+
import type {DateRange} from '@astryxdesign/core/DateRangeInput';
|
|
8
|
+
import {Stack} from '@astryxdesign/core/Layout';
|
|
9
|
+
|
|
10
|
+
export default function DateRangeInputWithValidation() {
|
|
11
|
+
const [errorRange, setErrorRange] = useState<DateRange | null>({
|
|
12
|
+
start: '2026-01-01',
|
|
13
|
+
end: '2026-01-31',
|
|
14
|
+
});
|
|
15
|
+
const [warningRange, setWarningRange] = useState<DateRange | null>({
|
|
16
|
+
start: '2026-06-01',
|
|
17
|
+
end: '2026-06-30',
|
|
18
|
+
});
|
|
19
|
+
const [successRange, setSuccessRange] = useState<DateRange | null>({
|
|
20
|
+
start: '2026-03-01',
|
|
21
|
+
end: '2026-03-31',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Stack direction="vertical" gap={4} width="100%" style={{maxWidth: 400}}>
|
|
26
|
+
<DateRangeInput
|
|
27
|
+
label="Booking period"
|
|
28
|
+
value={errorRange}
|
|
29
|
+
onChange={setErrorRange}
|
|
30
|
+
status={{
|
|
31
|
+
type: 'error',
|
|
32
|
+
message: 'Selected dates are no longer available',
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
<DateRangeInput
|
|
36
|
+
label="Preferred period"
|
|
37
|
+
value={warningRange}
|
|
38
|
+
onChange={setWarningRange}
|
|
39
|
+
status={{
|
|
40
|
+
type: 'warning',
|
|
41
|
+
message: 'High demand — limited availability',
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
<DateRangeInput
|
|
45
|
+
label="Confirmed period"
|
|
46
|
+
value={successRange}
|
|
47
|
+
onChange={setSuccessRange}
|
|
48
|
+
status={{type: 'success', message: 'Dates confirmed and available'}}
|
|
49
|
+
/>
|
|
50
|
+
</Stack>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
exampleFor: 'DateTimeInput',
|
|
7
|
+
name: 'DateTimeInput — Validation',
|
|
8
|
+
displayName: 'DateTimeInput — Validation',
|
|
9
|
+
description:
|
|
10
|
+
'DateTimeInput in all three status states: error, warning, and success. Use to surface scheduling conflicts, caution the user about edge cases, or confirm a valid datetime.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: ['DateTimeInput', 'Layout'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import {DateTimeInput} from '@astryxdesign/core/DateTimeInput';
|
|
7
|
+
import type {ISODateTimeString} from '@astryxdesign/core/DateTimeInput';
|
|
8
|
+
import {Stack} from '@astryxdesign/core/Layout';
|
|
9
|
+
|
|
10
|
+
export default function DateTimeInputWithValidation() {
|
|
11
|
+
const [errorVal, setErrorVal] = useState<ISODateTimeString | undefined>(
|
|
12
|
+
'2026-01-25T09:00' as ISODateTimeString,
|
|
13
|
+
);
|
|
14
|
+
const [warningVal, setWarningVal] = useState<ISODateTimeString | undefined>(
|
|
15
|
+
'2026-12-25T14:00' as ISODateTimeString,
|
|
16
|
+
);
|
|
17
|
+
const [successVal, setSuccessVal] = useState<ISODateTimeString | undefined>(
|
|
18
|
+
'2026-03-10T10:30' as ISODateTimeString,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Stack direction="vertical" gap={4} width="100%" style={{maxWidth: 400}}>
|
|
23
|
+
<DateTimeInput
|
|
24
|
+
label="Meeting time"
|
|
25
|
+
value={errorVal}
|
|
26
|
+
onChange={setErrorVal}
|
|
27
|
+
status={{type: 'error', message: 'This time slot is already booked'}}
|
|
28
|
+
/>
|
|
29
|
+
<DateTimeInput
|
|
30
|
+
label="Preferred time"
|
|
31
|
+
value={warningVal}
|
|
32
|
+
onChange={setWarningVal}
|
|
33
|
+
status={{type: 'warning', message: 'This falls outside business hours'}}
|
|
34
|
+
/>
|
|
35
|
+
<DateTimeInput
|
|
36
|
+
label="Start time"
|
|
37
|
+
value={successVal}
|
|
38
|
+
onChange={setSuccessVal}
|
|
39
|
+
status={{type: 'success', message: 'Time confirmed'}}
|
|
40
|
+
/>
|
|
41
|
+
</Stack>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -10,5 +10,5 @@ export const doc = {
|
|
|
10
10
|
'Shows a page summary when hovering a link: title, description, and URL. Use for documentation links, article references, or any URL where a preview helps the user decide whether to click.',
|
|
11
11
|
isReady: true,
|
|
12
12
|
aspectRatio: 16 / 9,
|
|
13
|
-
componentsUsed: ['HoverCard', 'Icon', 'Layout', 'Text'],
|
|
13
|
+
componentsUsed: ['HoverCard', 'Icon', 'Layout', 'Link', 'Text'],
|
|
14
14
|
};
|
|
@@ -5,12 +5,14 @@
|
|
|
5
5
|
import {HoverCard} from '@astryxdesign/core/HoverCard';
|
|
6
6
|
import {Icon} from '@astryxdesign/core/Icon';
|
|
7
7
|
import {VStack, HStack} from '@astryxdesign/core/Layout';
|
|
8
|
+
import {Link} from '@astryxdesign/core/Link';
|
|
8
9
|
import {Text} from '@astryxdesign/core/Text';
|
|
9
10
|
import {LinkIcon} from '@heroicons/react/24/outline';
|
|
10
11
|
|
|
11
12
|
export default function HoverCardInteractiveContent() {
|
|
12
13
|
return (
|
|
13
|
-
<Text type="body">
|
|
14
|
+
<Text type="body">
|
|
15
|
+
Read more in the{' '}
|
|
14
16
|
<HoverCard
|
|
15
17
|
placement="below"
|
|
16
18
|
content={
|
|
@@ -30,8 +32,11 @@ export default function HoverCardInteractiveContent() {
|
|
|
30
32
|
</HStack>
|
|
31
33
|
</VStack>
|
|
32
34
|
}>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
<Link href="#" hasUnderline>
|
|
36
|
+
Getting Started Guide
|
|
37
|
+
</Link>
|
|
38
|
+
</HoverCard>
|
|
39
|
+
.
|
|
40
|
+
</Text>
|
|
36
41
|
);
|
|
37
42
|
}
|
|
@@ -7,7 +7,7 @@ export const doc = {
|
|
|
7
7
|
name: 'Link Provider — Custom Link Component',
|
|
8
8
|
displayName: 'Link Provider — Custom Link Component',
|
|
9
9
|
description:
|
|
10
|
-
'Routes every Astryx link through a custom component that intercepts the click
|
|
10
|
+
'Routes every Astryx link through a custom component that intercepts the click, the hook frameworks like Next.js use for client-side navigation. Click the link to see the custom handler fire instead of a full-page load.',
|
|
11
11
|
isReady: true,
|
|
12
12
|
aspectRatio: 4 / 3,
|
|
13
13
|
componentsUsed: ['LinkProvider', 'Link'],
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/** @type {import('../../../../../core/src/docs-types').TemplateDoc} */
|
|
4
|
+
export const doc = {
|
|
5
|
+
type: 'block',
|
|
6
|
+
exampleFor: 'Tab',
|
|
7
|
+
name: 'Tab — Selected Icon',
|
|
8
|
+
displayName: 'Tab — Selected Icon',
|
|
9
|
+
description: 'A tab that changes its icon when selected.',
|
|
10
|
+
isReady: true,
|
|
11
|
+
aspectRatio: 4 / 3,
|
|
12
|
+
componentsUsed: ['TabList', 'Tab'],
|
|
13
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import {TabList, Tab} from '@astryxdesign/core/TabList';
|
|
7
|
+
|
|
8
|
+
const StarIcon = (
|
|
9
|
+
<svg
|
|
10
|
+
viewBox="0 0 16 16"
|
|
11
|
+
fill="none"
|
|
12
|
+
stroke="currentColor"
|
|
13
|
+
width="100%"
|
|
14
|
+
height="100%">
|
|
15
|
+
<path d="m8 1.5 2 4.1 4.5.7-3.3 3.2.8 4.5-4-2.1L4 14l.8-4.5-3.3-3.2 4.5-.7L8 1.5Z" />
|
|
16
|
+
</svg>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const SelectedStarIcon = (
|
|
20
|
+
<svg viewBox="0 0 16 16" fill="currentColor" width="100%" height="100%">
|
|
21
|
+
<path d="m8 1.5 2 4.1 4.5.7-3.3 3.2.8 4.5-4-2.1L4 14l.8-4.5-3.3-3.2 4.5-.7L8 1.5Z" />
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export default function TabWithSelectedIcon() {
|
|
26
|
+
const [value, setValue] = useState('favorites');
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<TabList value={value} onChange={setValue}>
|
|
30
|
+
<Tab
|
|
31
|
+
value="favorites"
|
|
32
|
+
label="Favorites"
|
|
33
|
+
icon={StarIcon}
|
|
34
|
+
selectedIcon={SelectedStarIcon}
|
|
35
|
+
/>
|
|
36
|
+
<Tab value="recent" label="Recent" />
|
|
37
|
+
</TabList>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -7,7 +7,7 @@ export const doc = {
|
|
|
7
7
|
name: 'useTableStickyColumns — Pinned Columns',
|
|
8
8
|
displayName: 'useTableStickyColumns — Pinned Columns',
|
|
9
9
|
description:
|
|
10
|
-
'A Table using useTableStickyColumns to pin the Name column to the start edge and Status to the end edge. Scroll horizontally
|
|
10
|
+
'A Table using useTableStickyColumns to pin the Name column to the start edge and Status to the end edge. Scroll horizontally; pinned columns stay in view with a soft shadow over the scrolling content.',
|
|
11
11
|
isReady: true,
|
|
12
12
|
aspectRatio: 16 / 9,
|
|
13
13
|
componentsUsed: ['Table'],
|
|
@@ -6,7 +6,7 @@ export const doc = {
|
|
|
6
6
|
name: 'Side Nav',
|
|
7
7
|
displayName: 'Side Nav',
|
|
8
8
|
description:
|
|
9
|
-
'A side-nav app shell with a collapsible, resizable SideNav
|
|
9
|
+
'A side-nav app shell with a collapsible, resizable SideNav (new chat, search, library, and workspace-grouped conversations with status dots) over static grey-card conversation and composer placeholders.',
|
|
10
10
|
isReady: true,
|
|
11
11
|
category: 'Shell - Left Sidebar',
|
|
12
12
|
};
|
|
@@ -6,7 +6,7 @@ export const doc = {
|
|
|
6
6
|
name: 'Top Nav',
|
|
7
7
|
displayName: 'Top Nav',
|
|
8
8
|
description:
|
|
9
|
-
'A top-nav app shell with a centered TopNav
|
|
9
|
+
'A top-nav app shell with a centered TopNav (Shop and Brands mega menus with a 2x4 item grid plus a featured card each, Sale/Service links, and a Checkout button) over static grey-card hero and category sections.',
|
|
10
10
|
isReady: true,
|
|
11
11
|
category: 'Shell - Top Nav',
|
|
12
12
|
};
|