@astryxdesign/cli 0.1.6 → 0.1.7-canary.04cd8f7
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 +33 -0
- package/README.md +115 -19
- package/docs/cli-integrations.doc.mjs +150 -0
- package/docs/getting-started.doc.mjs +9 -9
- package/docs/internationalization.doc.mjs +243 -0
- package/docs/layout.doc.dense.mjs +5 -0
- package/docs/migration.doc.mjs +18 -18
- package/docs/principles.doc.dense.mjs +5 -5
- package/docs/principles.doc.mjs +6 -6
- package/docs/principles.doc.zh.mjs +5 -5
- package/docs/styling-libraries.doc.mjs +3 -3
- package/docs/styling.doc.mjs +4 -4
- package/docs/theme.doc.dense.mjs +12 -12
- package/docs/theme.doc.mjs +7 -7
- package/docs/theme.doc.zh.mjs +10 -10
- package/docs/tokens.doc.dense.mjs +6 -7
- package/docs/tokens.doc.mjs +1 -1
- package/docs/tokens.doc.zh.mjs +6 -7
- package/docs/working-with-ai.doc.mjs +18 -18
- package/package.json +13 -10
- package/src/api/docOverlays.test.mjs +133 -0
- package/src/api/docs.mjs +14 -2
- package/src/api/doctor.mjs +3 -3
- package/src/api/integration-block-exports.test.mjs +240 -0
- package/src/api/template-suffix.test.mjs +246 -0
- package/src/api/template.mjs +104 -28
- package/src/api/validate-integration.mjs +0 -8
- package/src/codemods/__tests__/registry.test.mjs +1 -0
- package/src/codemods/ensure-jscodeshift.mjs +11 -27
- package/src/codemods/registry.mjs +1 -0
- package/src/codemods/run-codemod.mjs +1 -1
- package/src/codemods/runner.mjs +2 -2
- package/src/codemods/transforms/v0.1.7/__tests__/migrate-table-tableprops-to-direct-props.test.mjs +120 -0
- package/src/codemods/transforms/v0.1.7/__tests__/rename-table-renderprops-styles-to-xstyle.test.mjs +112 -0
- package/src/codemods/transforms/v0.1.7/index.mjs +27 -0
- package/src/codemods/transforms/v0.1.7/migrate-table-tableprops-to-direct-props.mjs +188 -0
- package/src/codemods/transforms/v0.1.7/rename-table-renderprops-styles-to-xstyle.mjs +197 -0
- package/src/commands/agent-docs.mjs +21 -8
- package/src/commands/agent-docs.test.mjs +31 -4
- package/src/commands/build-theme.mjs +10 -71
- package/src/commands/build.mjs +15 -15
- package/src/commands/component/index.mjs +4 -4
- package/src/commands/discover.mjs +7 -5
- package/src/commands/docs.mjs +4 -4
- package/src/commands/hook/index.mjs +4 -4
- package/src/commands/init.mjs +48 -152
- package/src/commands/init.next-steps.test.mjs +1 -1
- package/src/commands/interactive-guard.test.mjs +19 -22
- package/src/commands/json-contract.test.mjs +1 -1
- package/src/commands/layout.mjs +1 -1
- package/src/commands/search.mjs +4 -4
- package/src/commands/swizzle.mjs +11 -34
- package/src/commands/template.mjs +11 -31
- package/src/commands/upgrade.mjs +9 -6
- package/src/commands/upgrade.test.mjs +1 -1
- package/src/config.mjs +5 -14
- package/src/doc.mjs +27 -0
- package/src/doc.test.mjs +383 -0
- package/src/index.mjs +5 -6
- package/src/integration.mjs +4 -15
- package/src/lib/component-discovery.importpath.test.mjs +59 -0
- package/src/lib/component-discovery.mjs +15 -5
- package/src/lib/component-format.mjs +47 -14
- package/src/lib/component-format.test.mjs +95 -1
- package/src/lib/component-loader.mjs +104 -2
- package/src/lib/componentDocOverlay.test.mjs +111 -0
- package/src/lib/config-schema.mjs +0 -30
- package/src/lib/hook-format.mjs +8 -3
- package/src/lib/term-log.mjs +48 -0
- package/src/lib/xle/registry.mjs +0 -5
- package/src/schemas/doc-schema.mjs +226 -0
- package/src/schemas/template-schema.mjs +47 -0
- package/src/template.mjs +9 -67
- package/src/types/config.d.ts +11 -66
- package/src/types/doc.d.ts +23 -0
- package/src/types/integration.d.ts +7 -18
- package/src/types/template-api.d.ts +14 -50
- package/src/utils/package-manager.mjs +78 -0
- package/src/utils/package-manager.test.mjs +108 -1
- package/src/utils/path-safety.mjs +0 -18
- package/src/utils/update-check.mjs +2 -1
- package/templates/blocks/components/Avatar/AvatarGroup.tsx +5 -7
- package/templates/blocks/components/Avatar/AvatarShowcase.tsx +4 -6
- package/templates/blocks/components/Avatar/AvatarUserCard.tsx +3 -5
- package/templates/blocks/components/Avatar/AvatarWithImage.tsx +8 -6
- package/templates/blocks/components/Avatar/AvatarWithStatus.tsx +3 -5
- package/templates/blocks/components/ChatComposerInput/ChatComposerInputControlledInput.tsx +1 -1
- package/templates/blocks/components/ChatComposerInput/ChatComposerInputDisabled.tsx +1 -1
- package/templates/blocks/components/ChatComposerInput/ChatComposerInputMentionTrigger.tsx +1 -1
- package/templates/blocks/components/ChatComposerInput/ChatComposerInputMultipleTriggers.tsx +1 -1
- package/templates/blocks/components/ChatComposerInput/ChatComposerInputShowcase.tsx +1 -1
- package/templates/blocks/components/ChatComposerInput/ChatComposerInputSlashCommands.tsx +1 -1
- package/templates/blocks/components/TabList/TabListTabsWithActions.doc.mjs +1 -1
- package/templates/blocks/components/TabList/TabListTabsWithActions.tsx +2 -7
- package/templates/blocks/components/VisuallyHidden/VisuallyHiddenLiveRegion.doc.mjs +14 -0
- package/templates/blocks/components/VisuallyHidden/VisuallyHiddenLiveRegion.tsx +41 -0
- package/templates/blocks/components/VisuallyHidden/VisuallyHiddenShowcase.doc.mjs +13 -0
- package/templates/blocks/components/VisuallyHidden/VisuallyHiddenShowcase.tsx +78 -0
- package/templates/blocks/components/VisuallyHidden/VisuallyHiddenStructuralHeading.doc.mjs +14 -0
- package/templates/blocks/components/VisuallyHidden/VisuallyHiddenStructuralHeading.tsx +38 -0
- package/templates/blocks/components/VisuallyHidden/VisuallyHiddenSupplementaryContext.doc.mjs +14 -0
- package/templates/blocks/components/VisuallyHidden/VisuallyHiddenSupplementaryContext.tsx +47 -0
- package/templates/pages/ide/page.tsx +35 -41
- package/templates/pages/theme-showcase/page.tsx +7 -7
- package/templates/themes/neutral/neutralTheme.ts +63 -32
- package/src/utils/interactive.mjs +0 -76
- package/src/utils/interactive.test.mjs +0 -70
|
@@ -4,9 +4,16 @@ import {describe, it, expect, afterEach, vi} from 'vitest';
|
|
|
4
4
|
import * as fs from 'node:fs';
|
|
5
5
|
import * as path from 'node:path';
|
|
6
6
|
import * as os from 'node:os';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
detectPackageManager,
|
|
9
|
+
getDlxPrefix,
|
|
10
|
+
isCliOneOff,
|
|
11
|
+
getCliInvocation,
|
|
12
|
+
formatCliCommand,
|
|
13
|
+
} from './package-manager.mjs';
|
|
8
14
|
|
|
9
15
|
let tmpDir;
|
|
16
|
+
const ORIGINAL_ARGV1 = process.argv[1];
|
|
10
17
|
|
|
11
18
|
afterEach(() => {
|
|
12
19
|
if (tmpDir) {
|
|
@@ -15,6 +22,7 @@ afterEach(() => {
|
|
|
15
22
|
}
|
|
16
23
|
vi.restoreAllMocks();
|
|
17
24
|
delete process.env.npm_config_user_agent;
|
|
25
|
+
process.argv[1] = ORIGINAL_ARGV1;
|
|
18
26
|
});
|
|
19
27
|
|
|
20
28
|
function makeTmpDir() {
|
|
@@ -111,3 +119,102 @@ describe('detectPackageManager', () => {
|
|
|
111
119
|
expect(detectPackageManager(dir)).toBe('bun');
|
|
112
120
|
});
|
|
113
121
|
});
|
|
122
|
+
|
|
123
|
+
describe('getDlxPrefix', () => {
|
|
124
|
+
it('returns "pnpm dlx" for pnpm projects', () => {
|
|
125
|
+
const dir = makeTmpDir();
|
|
126
|
+
fs.writeFileSync(path.join(dir, 'pnpm-lock.yaml'), '');
|
|
127
|
+
expect(getDlxPrefix(dir)).toBe('pnpm dlx');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('returns "yarn dlx" for yarn projects', () => {
|
|
131
|
+
const dir = makeTmpDir();
|
|
132
|
+
fs.writeFileSync(path.join(dir, 'yarn.lock'), '');
|
|
133
|
+
expect(getDlxPrefix(dir)).toBe('yarn dlx');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('returns "bunx" for bun projects', () => {
|
|
137
|
+
const dir = makeTmpDir();
|
|
138
|
+
fs.writeFileSync(path.join(dir, 'bun.lockb'), '');
|
|
139
|
+
expect(getDlxPrefix(dir)).toBe('bunx');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('falls back to "npx" with no signals', () => {
|
|
143
|
+
const dir = makeTmpDir();
|
|
144
|
+
delete process.env.npm_config_user_agent;
|
|
145
|
+
expect(getDlxPrefix(dir)).toBe('npx');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('isCliOneOff', () => {
|
|
150
|
+
it('detects an npm npx cache entry', () => {
|
|
151
|
+
process.argv[1] = '/home/u/.npm/_npx/a1b2/node_modules/.bin/astryx';
|
|
152
|
+
expect(isCliOneOff()).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('detects a pnpm dlx cache entry', () => {
|
|
156
|
+
process.argv[1] = '/home/u/.cache/pnpm/dlx/9f/node_modules/@astryxdesign/cli/bin/astryx.mjs';
|
|
157
|
+
expect(isCliOneOff()).toBe(true);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('detects a bunx cache entry', () => {
|
|
161
|
+
process.argv[1] = '/home/u/.bun/install/cache/@astryxdesign/cli/bin/astryx.mjs';
|
|
162
|
+
expect(isCliOneOff()).toBe(true);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('is false for an installed node_modules entry', () => {
|
|
166
|
+
process.argv[1] = '/proj/node_modules/@astryxdesign/cli/bin/astryx.mjs';
|
|
167
|
+
expect(isCliOneOff()).toBe(false);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('is false for a source checkout (dev) entry', () => {
|
|
171
|
+
process.argv[1] = '/repo/packages/cli/bin/astryx.mjs';
|
|
172
|
+
expect(isCliOneOff()).toBe(false);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('getCliInvocation', () => {
|
|
177
|
+
it('uses the run-prefix + bare bin when installed (not one-off)', () => {
|
|
178
|
+
process.argv[1] = '/proj/node_modules/@astryxdesign/cli/bin/astryx.mjs';
|
|
179
|
+
const dir = makeTmpDir();
|
|
180
|
+
fs.writeFileSync(path.join(dir, 'pnpm-lock.yaml'), '');
|
|
181
|
+
expect(getCliInvocation(dir)).toBe('pnpm exec astryx');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('uses the dlx runner + scoped package when run one-off', () => {
|
|
185
|
+
process.argv[1] = '/home/u/.npm/_npx/a1b2/node_modules/.bin/astryx';
|
|
186
|
+
const dir = makeTmpDir();
|
|
187
|
+
delete process.env.npm_config_user_agent;
|
|
188
|
+
expect(getCliInvocation(dir)).toBe('npx @astryxdesign/cli');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('pairs the dlx runner with the scoped package for pnpm one-off', () => {
|
|
192
|
+
process.argv[1] = '/home/u/.cache/pnpm/dlx/9f/node_modules/@astryxdesign/cli/bin/astryx.mjs';
|
|
193
|
+
const dir = makeTmpDir();
|
|
194
|
+
fs.writeFileSync(path.join(dir, 'pnpm-lock.yaml'), '');
|
|
195
|
+
expect(getCliInvocation(dir)).toBe('pnpm dlx @astryxdesign/cli');
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe('formatCliCommand', () => {
|
|
200
|
+
it('strips a leading "astryx" token and prepends the invocation stem', () => {
|
|
201
|
+
process.argv[1] = '/proj/node_modules/@astryxdesign/cli/bin/astryx.mjs';
|
|
202
|
+
const dir = makeTmpDir();
|
|
203
|
+
fs.writeFileSync(path.join(dir, 'pnpm-lock.yaml'), '');
|
|
204
|
+
expect(formatCliCommand('astryx component Button', dir)).toBe('pnpm exec astryx component Button');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('accepts a bare subcommand (no leading astryx)', () => {
|
|
208
|
+
process.argv[1] = '/proj/node_modules/@astryxdesign/cli/bin/astryx.mjs';
|
|
209
|
+
const dir = makeTmpDir();
|
|
210
|
+
fs.writeFileSync(path.join(dir, 'package-lock.json'), '{}');
|
|
211
|
+
expect(formatCliCommand('docs tokens', dir)).toBe('npx astryx docs tokens');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('rewrites to the scoped package for one-off invocations', () => {
|
|
215
|
+
process.argv[1] = '/home/u/.npm/_npx/a1b2/node_modules/.bin/astryx';
|
|
216
|
+
const dir = makeTmpDir();
|
|
217
|
+
fs.writeFileSync(path.join(dir, 'package-lock.json'), '{}');
|
|
218
|
+
expect(formatCliCommand('astryx component Button', dir)).toBe('npx @astryxdesign/cli component Button');
|
|
219
|
+
});
|
|
220
|
+
});
|
|
@@ -165,21 +165,3 @@ export function isFilePathArg(pathArg) {
|
|
|
165
165
|
const ext = path.extname(base).toLowerCase();
|
|
166
166
|
return ext.length > 0 && FILE_EXTENSIONS.has(ext);
|
|
167
167
|
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* True when the process is running non-interactively (no TTY) or when the
|
|
171
|
-
* caller has signaled JSON / scripted use. Commands consult this before
|
|
172
|
-
* prompting for confirmation; in scripted mode they require an explicit
|
|
173
|
-
* `--overwrite` flag instead.
|
|
174
|
-
*
|
|
175
|
-
* @param {object} [options]
|
|
176
|
-
* @param {boolean} [options.json] - Caller's --json flag.
|
|
177
|
-
* @returns {boolean}
|
|
178
|
-
*/
|
|
179
|
-
export function isNonInteractive({json = false} = {}) {
|
|
180
|
-
if (json) return true;
|
|
181
|
-
// stdin not a TTY means piped input or scripted execution.
|
|
182
|
-
if (process.stdin && process.stdin.isTTY === false) return true;
|
|
183
|
-
if (process.stdout && process.stdout.isTTY === false) return true;
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import * as fs from 'node:fs';
|
|
15
15
|
import * as path from 'node:path';
|
|
16
16
|
import {semverGt} from './semver.mjs';
|
|
17
|
+
import {getCliInvocation} from './package-manager.mjs';
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Read the latest available version from local signals.
|
|
@@ -75,7 +76,7 @@ export function checkForUpdate(cwd = process.cwd()) {
|
|
|
75
76
|
// Use semver-aware comparison so '0.0.20' is correctly treated as greater
|
|
76
77
|
// than '0.0.5' (lexicographic compare gets that backwards).
|
|
77
78
|
if (semverGt(latest, installed)) {
|
|
78
|
-
return `FYI: A newer version of @astryxdesign/core (${latest}) is available. Install the new package version, then run:
|
|
79
|
+
return `FYI: A newer version of @astryxdesign/core (${latest}) is available. Install the new package version, then run: ${getCliInvocation()} upgrade --from <old-version> --apply`;
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
return null;
|
|
@@ -7,28 +7,26 @@ import {AvatarGroup, AvatarGroupOverflow} from '@astryxdesign/core/AvatarGroup';
|
|
|
7
7
|
import {Stack} from '@astryxdesign/core/Layout';
|
|
8
8
|
import {Text} from '@astryxdesign/core/Text';
|
|
9
9
|
|
|
10
|
-
const CDN = 'https://lookaside.facebook.com/assets/astryx';
|
|
11
|
-
|
|
12
10
|
const USERS = [
|
|
13
11
|
{
|
|
14
12
|
name: 'Ami Pena',
|
|
15
|
-
src:
|
|
13
|
+
src: 'https://lookaside.facebook.com/assets/astryx/DATA-Ami-Pena.png',
|
|
16
14
|
},
|
|
17
15
|
{
|
|
18
16
|
name: 'Drew Young',
|
|
19
|
-
src:
|
|
17
|
+
src: 'https://lookaside.facebook.com/assets/astryx/DATA-Drew-Young.png',
|
|
20
18
|
},
|
|
21
19
|
{
|
|
22
20
|
name: 'Gabriela Fernandez',
|
|
23
|
-
src:
|
|
21
|
+
src: 'https://lookaside.facebook.com/assets/astryx/DATA-Gabriela-Fernandez.png',
|
|
24
22
|
},
|
|
25
23
|
{
|
|
26
24
|
name: 'Jihoo Song',
|
|
27
|
-
src:
|
|
25
|
+
src: 'https://lookaside.facebook.com/assets/astryx/DATA-Jihoo-Song.png',
|
|
28
26
|
},
|
|
29
27
|
{
|
|
30
28
|
name: 'Nam Tran',
|
|
31
|
-
src:
|
|
29
|
+
src: 'https://lookaside.facebook.com/assets/astryx/DATA-Nam-Tran.png',
|
|
32
30
|
},
|
|
33
31
|
];
|
|
34
32
|
|
|
@@ -5,29 +5,27 @@
|
|
|
5
5
|
import {Avatar, AvatarStatusDot} from '@astryxdesign/core/Avatar';
|
|
6
6
|
import {Stack} from '@astryxdesign/core/Layout';
|
|
7
7
|
|
|
8
|
-
const CDN = 'https://lookaside.facebook.com/assets/astryx';
|
|
9
|
-
|
|
10
8
|
export default function AvatarShowcase() {
|
|
11
9
|
return (
|
|
12
10
|
<Stack direction="horizontal" gap={4} vAlign="center">
|
|
13
11
|
<Avatar
|
|
14
|
-
src=
|
|
12
|
+
src="https://lookaside.facebook.com/assets/astryx/DATA-Ana-Thomas.png"
|
|
15
13
|
name="Ana Thomas"
|
|
16
14
|
size="large"
|
|
17
15
|
status={<AvatarStatusDot variant="success" label="Online" />}
|
|
18
16
|
/>
|
|
19
17
|
<Avatar
|
|
20
|
-
src=
|
|
18
|
+
src="https://lookaside.facebook.com/assets/astryx/DATA-Drew-Young.png"
|
|
21
19
|
name="Drew Young"
|
|
22
20
|
size="large"
|
|
23
21
|
/>
|
|
24
22
|
<Avatar
|
|
25
|
-
src=
|
|
23
|
+
src="https://lookaside.facebook.com/assets/astryx/DATA-Jihoo-Song.png"
|
|
26
24
|
name="Jihoo Song"
|
|
27
25
|
size="large"
|
|
28
26
|
/>
|
|
29
27
|
<Avatar
|
|
30
|
-
src=
|
|
28
|
+
src="https://lookaside.facebook.com/assets/astryx/DATA-Nam-Tran.png"
|
|
31
29
|
name="Nam Tran"
|
|
32
30
|
size="large"
|
|
33
31
|
status={<AvatarStatusDot variant="error" label="Online" />}
|
|
@@ -6,24 +6,22 @@ import {Avatar, AvatarStatusDot} from '@astryxdesign/core/Avatar';
|
|
|
6
6
|
import {Stack} from '@astryxdesign/core/Layout';
|
|
7
7
|
import {Text} from '@astryxdesign/core/Text';
|
|
8
8
|
|
|
9
|
-
const CDN = 'https://lookaside.facebook.com/assets/astryx';
|
|
10
|
-
|
|
11
9
|
const USERS = [
|
|
12
10
|
{
|
|
13
11
|
name: 'Itai Jordaan',
|
|
14
|
-
src:
|
|
12
|
+
src: 'https://lookaside.facebook.com/assets/astryx/DATA-Itai-Jordaan.png',
|
|
15
13
|
role: 'Engineering Lead',
|
|
16
14
|
variant: 'success' as const,
|
|
17
15
|
},
|
|
18
16
|
{
|
|
19
17
|
name: 'Margot Schroder',
|
|
20
|
-
src:
|
|
18
|
+
src: 'https://lookaside.facebook.com/assets/astryx/DATA-Margot-Schroder.png',
|
|
21
19
|
role: 'Product Designer',
|
|
22
20
|
variant: 'neutral' as const,
|
|
23
21
|
},
|
|
24
22
|
{
|
|
25
23
|
name: 'Daniela Gimenez',
|
|
26
|
-
src:
|
|
24
|
+
src: 'https://lookaside.facebook.com/assets/astryx/DATA-Daniela-Gimenez.png',
|
|
27
25
|
role: 'Engineering Manager',
|
|
28
26
|
variant: 'error' as const,
|
|
29
27
|
},
|
|
@@ -5,24 +5,26 @@
|
|
|
5
5
|
import {Avatar} from '@astryxdesign/core/Avatar';
|
|
6
6
|
import {Stack} from '@astryxdesign/core/Layout';
|
|
7
7
|
|
|
8
|
-
const CDN = 'https://lookaside.facebook.com/assets/astryx';
|
|
9
|
-
|
|
10
8
|
export default function AvatarWithImage() {
|
|
11
9
|
return (
|
|
12
10
|
<Stack direction="horizontal" gap={4} vAlign="center">
|
|
13
|
-
<Avatar src={`${CDN}/DATA-Ami-Pena.png`} name="Ami Pena" size="tiny" />
|
|
14
11
|
<Avatar
|
|
15
|
-
src=
|
|
12
|
+
src="https://lookaside.facebook.com/assets/astryx/DATA-Ami-Pena.png"
|
|
13
|
+
name="Ami Pena"
|
|
14
|
+
size="tiny"
|
|
15
|
+
/>
|
|
16
|
+
<Avatar
|
|
17
|
+
src="https://lookaside.facebook.com/assets/astryx/DATA-Ana-Thomas.png"
|
|
16
18
|
name="Ana Thomas"
|
|
17
19
|
size="small"
|
|
18
20
|
/>
|
|
19
21
|
<Avatar
|
|
20
|
-
src=
|
|
22
|
+
src="https://lookaside.facebook.com/assets/astryx/DATA-Daniela-Gimenez.png"
|
|
21
23
|
name="Daniela Gimenez"
|
|
22
24
|
size="medium"
|
|
23
25
|
/>
|
|
24
26
|
<Avatar
|
|
25
|
-
src=
|
|
27
|
+
src="https://lookaside.facebook.com/assets/astryx/DATA-Gabriela-Fernandez.png"
|
|
26
28
|
name="Gabriela Fernandez"
|
|
27
29
|
size="large"
|
|
28
30
|
/>
|
|
@@ -5,25 +5,23 @@
|
|
|
5
5
|
import {Avatar, AvatarStatusDot} from '@astryxdesign/core/Avatar';
|
|
6
6
|
import {Stack} from '@astryxdesign/core/Layout';
|
|
7
7
|
|
|
8
|
-
const CDN = 'https://lookaside.facebook.com/assets/astryx';
|
|
9
|
-
|
|
10
8
|
export default function AvatarWithStatus() {
|
|
11
9
|
return (
|
|
12
10
|
<Stack direction="horizontal" gap={4} vAlign="center">
|
|
13
11
|
<Avatar
|
|
14
|
-
src=
|
|
12
|
+
src="https://lookaside.facebook.com/assets/astryx/DATA-Itai-Jordaan.png"
|
|
15
13
|
name="Itai Jordaan"
|
|
16
14
|
size="large"
|
|
17
15
|
status={<AvatarStatusDot variant="success" label="Online" />}
|
|
18
16
|
/>
|
|
19
17
|
<Avatar
|
|
20
|
-
src=
|
|
18
|
+
src="https://lookaside.facebook.com/assets/astryx/DATA-Margot-Schroder.png"
|
|
21
19
|
name="Margot Schroder"
|
|
22
20
|
size="large"
|
|
23
21
|
status={<AvatarStatusDot variant="neutral" label="Offline" />}
|
|
24
22
|
/>
|
|
25
23
|
<Avatar
|
|
26
|
-
src=
|
|
24
|
+
src="https://lookaside.facebook.com/assets/astryx/DATA-Pablo-Morales.png"
|
|
27
25
|
name="Pablo Morales"
|
|
28
26
|
size="large"
|
|
29
27
|
status={<AvatarStatusDot variant="error" label="Busy" />}
|
|
@@ -10,7 +10,7 @@ import {Text} from '@astryxdesign/core/Text';
|
|
|
10
10
|
export default function ChatComposerInputControlledInput() {
|
|
11
11
|
const [value, setValue] = useState('');
|
|
12
12
|
return (
|
|
13
|
-
<Stack direction="vertical" gap={3} style={{width:
|
|
13
|
+
<Stack direction="vertical" gap={3} style={{width: 450, maxWidth: '100%'}}>
|
|
14
14
|
<ChatComposer
|
|
15
15
|
onSubmit={() => setValue('')}
|
|
16
16
|
value={value}
|
|
@@ -7,7 +7,7 @@ import {Stack} from '@astryxdesign/core/Layout';
|
|
|
7
7
|
|
|
8
8
|
export default function ChatComposerInputDisabled() {
|
|
9
9
|
return (
|
|
10
|
-
<Stack direction="vertical" style={{width:
|
|
10
|
+
<Stack direction="vertical" style={{width: 450, maxWidth: '100%'}}>
|
|
11
11
|
<ChatComposer
|
|
12
12
|
onSubmit={() => {}}
|
|
13
13
|
isDisabled
|
|
@@ -43,7 +43,7 @@ export default function ChatComposerInputMentionTrigger() {
|
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
|
-
<Stack direction="vertical" gap={3} style={{width:
|
|
46
|
+
<Stack direction="vertical" gap={3} style={{width: 450, maxWidth: '100%'}}>
|
|
47
47
|
<ChatComposer
|
|
48
48
|
onSubmit={() => setValue('')}
|
|
49
49
|
input={
|
|
@@ -54,7 +54,7 @@ export default function ChatComposerInputMultipleTriggers() {
|
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
return (
|
|
57
|
-
<Stack direction="vertical" gap={3} style={{width:
|
|
57
|
+
<Stack direction="vertical" gap={3} style={{width: 450, maxWidth: '100%'}}>
|
|
58
58
|
<Text type="supporting" color="secondary">
|
|
59
59
|
Type @ for mentions (blue) or / for commands (yellow)
|
|
60
60
|
</Text>
|
|
@@ -7,7 +7,7 @@ import {Stack} from '@astryxdesign/core/Layout';
|
|
|
7
7
|
|
|
8
8
|
export default function ChatComposerInputShowcase() {
|
|
9
9
|
return (
|
|
10
|
-
<Stack direction="vertical" style={{width:
|
|
10
|
+
<Stack direction="vertical" style={{width: 450, maxWidth: '100%'}}>
|
|
11
11
|
<ChatComposer
|
|
12
12
|
onSubmit={() => {}}
|
|
13
13
|
input={
|
|
@@ -40,7 +40,7 @@ export default function ChatComposerInputSlashCommands() {
|
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
|
-
<Stack direction="vertical" style={{width:
|
|
43
|
+
<Stack direction="vertical" style={{width: 450, maxWidth: '100%'}}>
|
|
44
44
|
<ChatComposer
|
|
45
45
|
onSubmit={() => {}}
|
|
46
46
|
input={
|
|
@@ -7,7 +7,7 @@ export const doc = {
|
|
|
7
7
|
name: 'TabList — With Actions',
|
|
8
8
|
displayName: 'TabList — With Actions',
|
|
9
9
|
description:
|
|
10
|
-
'Page header pattern with tabs on the left and action buttons pushed to the right. When hasDivider is true,
|
|
10
|
+
'Page header pattern with tabs on the left and action buttons pushed to the right. When hasDivider is true, match the Button size to the TabList size so the tabs and actions align to a shared baseline above the divider.',
|
|
11
11
|
isReady: true,
|
|
12
12
|
aspectRatio: 4 / 3,
|
|
13
13
|
componentsUsed: ['TabList', 'Tab', 'Button'],
|
|
@@ -55,16 +55,11 @@ export default function TabListTabsWithActions() {
|
|
|
55
55
|
<Button
|
|
56
56
|
label="Filter"
|
|
57
57
|
variant="ghost"
|
|
58
|
-
size="
|
|
58
|
+
size="lg"
|
|
59
59
|
icon={FilterIcon}
|
|
60
60
|
isIconOnly
|
|
61
61
|
/>
|
|
62
|
-
<Button
|
|
63
|
-
label="New item"
|
|
64
|
-
variant="primary"
|
|
65
|
-
size="sm"
|
|
66
|
-
icon={PlusIcon}
|
|
67
|
-
/>
|
|
62
|
+
<Button label="New item" variant="primary" size="lg" icon={PlusIcon} />
|
|
68
63
|
</div>
|
|
69
64
|
</TabList>
|
|
70
65
|
);
|
|
@@ -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: 'VisuallyHidden',
|
|
7
|
+
name: 'VisuallyHidden — Live Region',
|
|
8
|
+
displayName: 'VisuallyHidden — Live Region',
|
|
9
|
+
description:
|
|
10
|
+
'A polite aria-live region announces visual-only state changes to assistive technology.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: ['VisuallyHidden', 'Button', 'HStack', 'VStack', 'Text'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {useState} from 'react';
|
|
6
|
+
import {VisuallyHidden} from '@astryxdesign/core/VisuallyHidden';
|
|
7
|
+
import {Button} from '@astryxdesign/core/Button';
|
|
8
|
+
import {HStack, VStack} from '@astryxdesign/core/Layout';
|
|
9
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
10
|
+
|
|
11
|
+
const columns = ['Backlog', 'In progress', 'Done'] as const;
|
|
12
|
+
|
|
13
|
+
export default function VisuallyHiddenLiveRegion() {
|
|
14
|
+
const [column, setColumn] = useState(0);
|
|
15
|
+
const current = columns[column];
|
|
16
|
+
|
|
17
|
+
function move() {
|
|
18
|
+
setColumn(c => (c + 1) % columns.length);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<VStack gap={4} hAlign="start">
|
|
23
|
+
<Text type="supporting" color="secondary">
|
|
24
|
+
Drag-and-drop and other visual-only changes are silent to screen
|
|
25
|
+
readers. A live region narrates them.
|
|
26
|
+
</Text>
|
|
27
|
+
<HStack gap={3} vAlign="center">
|
|
28
|
+
<Button label="Move task" variant="secondary" onClick={move} />
|
|
29
|
+
<Text type="body">
|
|
30
|
+
Task is in{' '}
|
|
31
|
+
<Text as="span" weight="bold">
|
|
32
|
+
{current}
|
|
33
|
+
</Text>
|
|
34
|
+
</Text>
|
|
35
|
+
</HStack>
|
|
36
|
+
<VisuallyHidden as="div" aria-live="polite">
|
|
37
|
+
{`Task moved to ${current}`}
|
|
38
|
+
</VisuallyHidden>
|
|
39
|
+
</VStack>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -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: 'VisuallyHidden',
|
|
7
|
+
name: 'VisuallyHidden',
|
|
8
|
+
displayName: 'VisuallyHidden',
|
|
9
|
+
isReady: true,
|
|
10
|
+
aspectRatio: 16 / 9,
|
|
11
|
+
isShowcase: true,
|
|
12
|
+
componentsUsed: ['VisuallyHidden', 'IconButton', 'Card', 'HStack', 'VStack', 'Text', 'Icon'],
|
|
13
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {VisuallyHidden} from '@astryxdesign/core/VisuallyHidden';
|
|
6
|
+
import {IconButton} from '@astryxdesign/core/IconButton';
|
|
7
|
+
import {Card} from '@astryxdesign/core/Card';
|
|
8
|
+
import {HStack, VStack} from '@astryxdesign/core/Layout';
|
|
9
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
10
|
+
import {Icon} from '@astryxdesign/core/Icon';
|
|
11
|
+
import {
|
|
12
|
+
ArrowDownTrayIcon,
|
|
13
|
+
ShareIcon,
|
|
14
|
+
TrashIcon,
|
|
15
|
+
} from '@heroicons/react/24/outline';
|
|
16
|
+
import {SpeakerWaveIcon} from '@heroicons/react/24/solid';
|
|
17
|
+
|
|
18
|
+
const actions = [
|
|
19
|
+
{label: 'Download', icon: ArrowDownTrayIcon},
|
|
20
|
+
{label: 'Share', icon: ShareIcon},
|
|
21
|
+
{label: 'Delete', icon: TrashIcon},
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* VisuallyHidden is invisible by design, so this hero teaches the concept by
|
|
26
|
+
* contrast: the icon-only buttons are all a sighted user sees, while the
|
|
27
|
+
* caption spells out the accessible name each one exposes. A live
|
|
28
|
+
* <VisuallyHidden> region below announces the same names to assistive tech,
|
|
29
|
+
* so the demo genuinely exercises the component it documents.
|
|
30
|
+
*/
|
|
31
|
+
export default function VisuallyHiddenShowcase() {
|
|
32
|
+
return (
|
|
33
|
+
<VStack gap={5} hAlign="center">
|
|
34
|
+
<HStack gap={6} vAlign="stretch" wrap="wrap" hAlign="center">
|
|
35
|
+
<Card variant="muted">
|
|
36
|
+
<VStack gap={4} hAlign="center">
|
|
37
|
+
<Text type="supporting" color="secondary">
|
|
38
|
+
What you see
|
|
39
|
+
</Text>
|
|
40
|
+
<HStack gap={2}>
|
|
41
|
+
{actions.map(({label, icon}) => (
|
|
42
|
+
<IconButton
|
|
43
|
+
key={label}
|
|
44
|
+
label={label}
|
|
45
|
+
icon={<Icon icon={icon} />}
|
|
46
|
+
variant="ghost"
|
|
47
|
+
/>
|
|
48
|
+
))}
|
|
49
|
+
</HStack>
|
|
50
|
+
</VStack>
|
|
51
|
+
</Card>
|
|
52
|
+
|
|
53
|
+
<Card variant="muted">
|
|
54
|
+
<VStack gap={4} hAlign="start">
|
|
55
|
+
<HStack gap={2} vAlign="center">
|
|
56
|
+
<Icon icon={SpeakerWaveIcon} size="sm" />
|
|
57
|
+
<Text type="supporting" color="secondary">
|
|
58
|
+
What a screen reader hears
|
|
59
|
+
</Text>
|
|
60
|
+
</HStack>
|
|
61
|
+
<VStack gap={2}>
|
|
62
|
+
{actions.map(({label}) => (
|
|
63
|
+
<Text key={label} type="body">
|
|
64
|
+
{label}, button
|
|
65
|
+
</Text>
|
|
66
|
+
))}
|
|
67
|
+
</VStack>
|
|
68
|
+
</VStack>
|
|
69
|
+
</Card>
|
|
70
|
+
</HStack>
|
|
71
|
+
|
|
72
|
+
{/* A real live region: silent to sighted users, announced by AT. */}
|
|
73
|
+
<VisuallyHidden as="div" aria-live="polite">
|
|
74
|
+
Actions available: Download, Share, Delete.
|
|
75
|
+
</VisuallyHidden>
|
|
76
|
+
</VStack>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -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: 'VisuallyHidden',
|
|
7
|
+
name: 'VisuallyHidden — Structural Heading',
|
|
8
|
+
displayName: 'VisuallyHidden — Structural Heading',
|
|
9
|
+
description:
|
|
10
|
+
'Give a visually implicit section an accessible name so screen-reader users can navigate to it.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: ['VisuallyHidden', 'Card', 'HStack', 'VStack', 'Text', 'Badge'],
|
|
14
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
'use client';
|
|
4
|
+
|
|
5
|
+
import {VisuallyHidden} from '@astryxdesign/core/VisuallyHidden';
|
|
6
|
+
import {Card} from '@astryxdesign/core/Card';
|
|
7
|
+
import {HStack, VStack} from '@astryxdesign/core/Layout';
|
|
8
|
+
import {Text} from '@astryxdesign/core/Text';
|
|
9
|
+
import {Badge} from '@astryxdesign/core/Badge';
|
|
10
|
+
|
|
11
|
+
const items = [
|
|
12
|
+
{name: 'astryx-core', status: 'Passing', variant: 'success'},
|
|
13
|
+
{name: 'astryx-charts', status: 'Failing', variant: 'error'},
|
|
14
|
+
{name: 'astryx-cli', status: 'Passing', variant: 'success'},
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
export default function VisuallyHiddenStructuralHeading() {
|
|
18
|
+
return (
|
|
19
|
+
<VStack gap={3} hAlign="start">
|
|
20
|
+
<Text type="supporting" color="secondary">
|
|
21
|
+
The layout makes this group obvious to sighted users. A hidden heading
|
|
22
|
+
gives screen-reader users the same landmark to jump to.
|
|
23
|
+
</Text>
|
|
24
|
+
{/* No visible heading is needed here, but AT users navigate by heading. */}
|
|
25
|
+
<VisuallyHidden as="h2">Build status</VisuallyHidden>
|
|
26
|
+
<VStack gap={2}>
|
|
27
|
+
{items.map(({name, status, variant}) => (
|
|
28
|
+
<Card key={name} variant="muted" padding={3}>
|
|
29
|
+
<HStack gap={3} vAlign="center">
|
|
30
|
+
<Text type="body">{name}</Text>
|
|
31
|
+
<Badge label={status} variant={variant} />
|
|
32
|
+
</HStack>
|
|
33
|
+
</Card>
|
|
34
|
+
))}
|
|
35
|
+
</VStack>
|
|
36
|
+
</VStack>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -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: 'VisuallyHidden',
|
|
7
|
+
name: 'VisuallyHidden — Supplementary Context',
|
|
8
|
+
displayName: 'VisuallyHidden — Supplementary Context',
|
|
9
|
+
description:
|
|
10
|
+
'Add screen-reader-only context to terse visual data, like spelling out what a trend arrow means.',
|
|
11
|
+
isReady: true,
|
|
12
|
+
aspectRatio: 4 / 3,
|
|
13
|
+
componentsUsed: ['VisuallyHidden', 'Card', 'HStack', 'VStack', 'Text', 'Icon'],
|
|
14
|
+
};
|