diff --git a/.changeset/remove-deprecated-apis.md b/.changeset/remove-deprecated-apis.md new file mode 100644 index 000000000000..42eb90b26b79 --- /dev/null +++ b/.changeset/remove-deprecated-apis.md @@ -0,0 +1,8 @@ +--- +'@astryxdesign/core': minor +'@astryxdesign/cli': minor +--- + +[breaking] Remove long-deprecated compatibility APIs from core and CLI. Run `astryx upgrade` first to migrate the supported replacements for authoring imports, Dialog logical positions, Switch label spacing, and Table root props. + +@cixzhang diff --git a/apps/sandbox/src/app/(raw)/pages/shell-lab/page.tsx b/apps/sandbox/src/app/(raw)/pages/shell-lab/page.tsx index 0ff753c38c59..135797836694 100644 --- a/apps/sandbox/src/app/(raw)/pages/shell-lab/page.tsx +++ b/apps/sandbox/src/app/(raw)/pages/shell-lab/page.tsx @@ -34,7 +34,7 @@ import {Badge} from '@astryxdesign/core/Badge'; import {Button} from '@astryxdesign/core/Button'; import {NavIcon} from '@astryxdesign/core/NavIcon'; import {Banner} from '@astryxdesign/core/Banner'; -import {NavMenuItem} from '@astryxdesign/core/NavMenu'; +import {NavHeadingMenuItem} from '@astryxdesign/core/NavMenu'; // ============================================================================= // Configuration types @@ -579,9 +579,9 @@ function SampleSideNav({ const headingMenu = ( <> - {}} /> - {}} /> - {}} /> + {}} /> + {}} /> + {}} /> ); @@ -630,9 +630,7 @@ function SampleSideNav({ resizable={config.isResizable} header={heading} topContent={ - config.showTopContent ? ( - - ) : undefined + config.showTopContent ? : undefined } footer={ config.showFooter ? ( @@ -844,9 +842,9 @@ function SampleTopNav({ const topNavHeadingMenu = ( <> - {}} /> - {}} /> - {}} /> + {}} /> + {}} /> + {}} /> ); @@ -1043,16 +1041,8 @@ function ShellLabContent() { href="#" icon={DashboardIcon} /> - - + + ), diff --git a/apps/sandbox/src/app/(sandbox)/pages/doc-preview/DocPreview.tsx b/apps/sandbox/src/app/(sandbox)/pages/doc-preview/DocPreview.tsx index 727c2a26b7a8..2a53ede0177e 100644 --- a/apps/sandbox/src/app/(sandbox)/pages/doc-preview/DocPreview.tsx +++ b/apps/sandbox/src/app/(sandbox)/pages/doc-preview/DocPreview.tsx @@ -8,7 +8,7 @@ import {VStack} from '@astryxdesign/core/Layout'; import {Text} from '@astryxdesign/core/Text'; import {Divider} from '@astryxdesign/core/Divider'; import type {DefinedTheme} from '@astryxdesign/core/theme'; -import type {ReferenceDoc, ReferenceSection} from '@astryxdesign/core'; +import type {ReferenceDoc, ReferenceSection} from '@astryxdesign/cli/authoring'; import {SectionRenderer} from './SectionRenderer'; import { ColorTokenTable, @@ -137,10 +137,7 @@ export function DocPreview({ {doc.title} {version && ( - + v{version} )} diff --git a/apps/sandbox/src/app/(sandbox)/pages/doc-preview/SectionRenderer.tsx b/apps/sandbox/src/app/(sandbox)/pages/doc-preview/SectionRenderer.tsx index bc4091bff2e1..926e7740ec28 100644 --- a/apps/sandbox/src/app/(sandbox)/pages/doc-preview/SectionRenderer.tsx +++ b/apps/sandbox/src/app/(sandbox)/pages/doc-preview/SectionRenderer.tsx @@ -6,7 +6,10 @@ import {useMemo} from 'react'; import * as stylex from '@stylexjs/stylex'; import {VStack} from '@astryxdesign/core/Layout'; import {Heading} from '@astryxdesign/core/Text'; -import type {ReferenceSection, ReferenceContentBlock} from '@astryxdesign/core'; +import type { + ReferenceSection, + ReferenceContentBlock, +} from '@astryxdesign/cli/authoring'; import {ContentBlockRenderer} from './content-blocks'; const styles = stylex.create({ diff --git a/apps/sandbox/src/app/(sandbox)/pages/doc-preview/content-blocks/ContentBlockRenderer.tsx b/apps/sandbox/src/app/(sandbox)/pages/doc-preview/content-blocks/ContentBlockRenderer.tsx index 8b9fc603e928..443b2747733b 100644 --- a/apps/sandbox/src/app/(sandbox)/pages/doc-preview/content-blocks/ContentBlockRenderer.tsx +++ b/apps/sandbox/src/app/(sandbox)/pages/doc-preview/content-blocks/ContentBlockRenderer.tsx @@ -2,7 +2,7 @@ 'use client'; -import type {ReferenceContentBlock} from '@astryxdesign/core'; +import type {ReferenceContentBlock} from '@astryxdesign/cli/authoring'; import {ProseBlock} from './ProseBlock'; import {CodeBlock} from './CodeBlock'; import {TableBlock} from './TableBlock'; diff --git a/apps/sandbox/src/app/(sandbox)/pages/doc-preview/page.tsx b/apps/sandbox/src/app/(sandbox)/pages/doc-preview/page.tsx index acf34ef08447..ecc10f700ada 100644 --- a/apps/sandbox/src/app/(sandbox)/pages/doc-preview/page.tsx +++ b/apps/sandbox/src/app/(sandbox)/pages/doc-preview/page.tsx @@ -1,61 +1,61 @@ // Copyright (c) Meta Platforms, Inc. and affiliates. -"use client"; +'use client'; -import { useState, useEffect } from "react"; -import * as stylex from "@stylexjs/stylex"; -import { HStack } from "@astryxdesign/core/Layout"; -import { Text } from "@astryxdesign/core/Text"; +import {useState, useEffect} from 'react'; +import * as stylex from '@stylexjs/stylex'; +import {HStack} from '@astryxdesign/core/Layout'; +import {Text} from '@astryxdesign/core/Text'; import { SegmentedControl, SegmentedControlItem, -} from "@astryxdesign/core/SegmentedControl"; -import { Spinner } from "@astryxdesign/core/Spinner"; -import type { ReferenceDoc } from "@astryxdesign/core"; -import { DocPreview } from "./DocPreview"; -import { ThemeContext } from "@astryxdesign/core/theme"; -import { useContext } from "react"; +} from '@astryxdesign/core/SegmentedControl'; +import {Spinner} from '@astryxdesign/core/Spinner'; +import type {ReferenceDoc} from '@astryxdesign/cli/authoring'; +import {DocPreview} from './DocPreview'; +import {ThemeContext} from '@astryxdesign/core/theme'; +import {useContext} from 'react'; const styles = stylex.create({ page: { - minHeight: "100vh", + minHeight: '100vh', }, topBar: { - position: "sticky", + position: 'sticky', top: 0, zIndex: 10, - backgroundColor: "var(--color-background-body)", - borderBottom: "1px solid var(--color-border)", - padding: "12px 32px", + backgroundColor: 'var(--color-background-body)', + borderBottom: '1px solid var(--color-border)', + padding: '12px 32px', }, loading: { - display: "flex", - alignItems: "center", - justifyContent: "center", + display: 'flex', + alignItems: 'center', + justifyContent: 'center', padding: 64, }, }); /** Foundational doc topics — the section docs that have tokenCategory */ const TOPICS = [ - { value: "color", label: "Color" }, - { value: "spacing", label: "Spacing" }, - { value: "typography", label: "Typography" }, - { value: "elevation", label: "Elevation" }, - { value: "shape", label: "Shape" }, - { value: "motion", label: "Motion" }, + {value: 'color', label: 'Color'}, + {value: 'spacing', label: 'Spacing'}, + {value: 'typography', label: 'Typography'}, + {value: 'elevation', label: 'Elevation'}, + {value: 'shape', label: 'Shape'}, + {value: 'motion', label: 'Motion'}, ] as const; export default function DocPreviewPage() { - const [topic, setTopic] = useState("color"); + const [topic, setTopic] = useState('color'); const [docs, setDocs] = useState>({}); const [version, setVersion] = useState(''); const [loading, setLoading] = useState(true); // Load pre-generated doc data useEffect(() => { - import("../../../../generated/foundationDocs.json") - .then((mod) => { + import('../../../../generated/foundationDocs.json') + .then(mod => { const data = mod.default as Record; setVersion((data.__version as string) ?? ''); const {__version: _, ...topics} = data; @@ -79,9 +79,8 @@ export default function DocPreviewPage() { value={topic} onChange={setTopic} label="Doc topic" - size="sm" - > - {TOPICS.map((t) => ( + size="sm"> + {TOPICS.map(t => ( ) : doc ? ( - ctx?.theme ? : null + ctx?.theme ? ( + + ) : null ) : (
No doc found for "{topic}" diff --git a/apps/storybook/stories/Dialog.stories.tsx b/apps/storybook/stories/Dialog.stories.tsx index 65ef5ace8f7d..6e328b15dc65 100644 --- a/apps/storybook/stories/Dialog.stories.tsx +++ b/apps/storybook/stories/Dialog.stories.tsx @@ -468,7 +468,7 @@ function PositionedModalExample() { setIsOpen(open)} - position={{top: 100, right: 20}} + position={{top: 100, end: 20}} width={350}> - This modal is positioned at top: 100px, right: 20px instead of + This modal is positioned at top: 100px, end: 20px instead of being centered. diff --git a/packages/cli/api/template/list/list.mjs b/packages/cli/api/template/list/list.mjs index 76c29a537e57..35d050c73a40 100644 --- a/packages/cli/api/template/list/list.mjs +++ b/packages/cli/api/template/list/list.mjs @@ -27,8 +27,6 @@ export function templateList(templates, options = {}) { data: filtered.map(t => ({ id: t.dirName, name: t.name, - // `displayName` retained for back-compat with existing consumers. - displayName: t.name, description: t.description, type: t.type, package: pkgOf(t), diff --git a/packages/cli/api/template/skeleton/skeleton.mjs b/packages/cli/api/template/skeleton/skeleton.mjs index d75b83758b40..6eed027c75b5 100644 --- a/packages/cli/api/template/skeleton/skeleton.mjs +++ b/packages/cli/api/template/skeleton/skeleton.mjs @@ -42,7 +42,6 @@ const SPATIAL_PROPS = [ 'rowGap', 'columnGap', 'columns', - 'minChildWidth', 'hasDivider', 'defaultHasDividers', 'variant', diff --git a/packages/cli/api/template/template.type.mjs b/packages/cli/api/template/template.type.mjs index 85899f4e9a72..2e07e6c67638 100644 --- a/packages/cli/api/template/template.type.mjs +++ b/packages/cli/api/template/template.type.mjs @@ -26,7 +26,6 @@ * @typedef {object} TemplateListEntry * @property {string} id - Stable template id (relative path under the templates root, minus the .doc.* suffix). * @property {string} name - * @property {string} displayName - (deprecated) Alias of `name`, retained for back-compat. * @property {string} description * @property {'page' | 'block'} type * @property {string} package - Owning package; core (built-in) templates report '@astryxdesign/core'. diff --git a/packages/cli/assets/codemods/transforms/next/__tests__/next-codemods.test.mjs b/packages/cli/assets/codemods/transforms/next/__tests__/next-codemods.test.mjs new file mode 100644 index 000000000000..0e0c01c9ce63 --- /dev/null +++ b/packages/cli/assets/codemods/transforms/next/__tests__/next-codemods.test.mjs @@ -0,0 +1,64 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +import {describe, expect, it} from 'vitest'; +import jscodeshift from 'jscodeshift'; + +const j = jscodeshift.withParser('tsx'); +const api = {jscodeshift: j, stats: () => {}, report: () => {}}; +async function apply(name, source) { + const {default: transform} = await import(`../${name}.mjs`); + return transform({source, path: 'test.tsx'}, api) ?? source; +} + +describe('next codemods', () => { + it('renames TopNavHeading href to headingHref without touching TopNavItem', async () => { + const input = `import {TopNavHeading, TopNavItem} from '@astryxdesign/core/TopNav'; +const x = <>;`; + const output = await apply( + 'rename-topnavheading-href-to-headinghref', + input, + ); + expect(output).toContain('headingHref="/"'); + expect(output).toContain(' { + const input = `import {Grid} from '@astryxdesign/core/Grid'; +const x = ;`; + const output = await apply('migrate-grid-minchildwidth-to-columns', input); + expect(output).toContain('columns={{'); + expect(output).toContain('minWidth: 160'); + expect(output).toContain("repeat: 'fit'"); + expect(output).not.toContain('minChildWidth'); + }); + + it('renames NavMenuItem imports and JSX', async () => { + const input = `import {NavMenuItem, type NavMenuItemProps} from '@astryxdesign/core/NavMenu'; +const item: NavMenuItemProps = {}; +const x = ;`; + const output = await apply( + 'migrate-navmenuitem-to-navheadingmenuitem', + input, + ); + expect(output).toContain('NavHeadingMenuItem'); + expect(output).toContain('NavHeadingMenuItemProps'); + expect(output).not.toContain('NavMenuItem'); + }); + + it('repoints lab CodeBlock imports to core CodeBlock subpath', async () => { + const input = `import {CodeBlock, Drawer} from '@astryxdesign/lab'; +const x = ;`; + const output = await apply('migrate-lab-codeblock-imports', input); + expect(output).toContain("from '@astryxdesign/core/CodeBlock'"); + expect(output).toMatch(/import \{\s*Drawer\s*\} from '@astryxdesign\/lab'/); + }); + + it('removes deprecated transition token imports', async () => { + const input = `import {transitionVars, durationVars, type TransitionVarName} from '@astryxdesign/core/theme'; +const d = durationVars;`; + const output = await apply('remove-theme-transition-token-imports', input); + expect(output).toContain('durationVars'); + expect(output).not.toContain('transitionVars'); + expect(output).not.toContain('TransitionVarName'); + }); +}); diff --git a/packages/cli/assets/codemods/transforms/next/index.mjs b/packages/cli/assets/codemods/transforms/next/index.mjs new file mode 100644 index 000000000000..bb87aaa23e57 --- /dev/null +++ b/packages/cli/assets/codemods/transforms/next/index.mjs @@ -0,0 +1,52 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** + * @file next transform manifest + * + * Staged codemods for the next release. The Version Packages PR promotes this + * file into the resolved version folder. + */ + +import renameTopNavHeadingHrefToHeadingHref, { + meta as renameTopNavHeadingHrefToHeadingHrefMeta, +} from './rename-topnavheading-href-to-headinghref.mjs'; +import migrateGridMinChildWidthToColumns, { + meta as migrateGridMinChildWidthToColumnsMeta, +} from './migrate-grid-minchildwidth-to-columns.mjs'; +import migrateNavMenuItemToNavHeadingMenuItem, { + meta as migrateNavMenuItemToNavHeadingMenuItemMeta, +} from './migrate-navmenuitem-to-navheadingmenuitem.mjs'; +import migrateLabCodeBlockImports, { + meta as migrateLabCodeBlockImportsMeta, +} from './migrate-lab-codeblock-imports.mjs'; +import removeThemeTransitionTokenImports, { + meta as removeThemeTransitionTokenImportsMeta, +} from './remove-theme-transition-token-imports.mjs'; + +export default [ + { + name: 'rename-topnavheading-href-to-headinghref', + transform: renameTopNavHeadingHrefToHeadingHref, + meta: renameTopNavHeadingHrefToHeadingHrefMeta, + }, + { + name: 'migrate-grid-minchildwidth-to-columns', + transform: migrateGridMinChildWidthToColumns, + meta: migrateGridMinChildWidthToColumnsMeta, + }, + { + name: 'migrate-navmenuitem-to-navheadingmenuitem', + transform: migrateNavMenuItemToNavHeadingMenuItem, + meta: migrateNavMenuItemToNavHeadingMenuItemMeta, + }, + { + name: 'migrate-lab-codeblock-imports', + transform: migrateLabCodeBlockImports, + meta: migrateLabCodeBlockImportsMeta, + }, + { + name: 'remove-theme-transition-token-imports', + transform: removeThemeTransitionTokenImports, + meta: removeThemeTransitionTokenImportsMeta, + }, +]; diff --git a/packages/cli/assets/codemods/transforms/next/migrate-grid-minchildwidth-to-columns.mjs b/packages/cli/assets/codemods/transforms/next/migrate-grid-minchildwidth-to-columns.mjs new file mode 100644 index 000000000000..46d6dd5feb6c --- /dev/null +++ b/packages/cli/assets/codemods/transforms/next/migrate-grid-minchildwidth-to-columns.mjs @@ -0,0 +1,83 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** + * @file Codemod: Migrate Grid minChildWidth to columns.minWidth + */ + +export const meta = { + title: 'Migrate Grid minChildWidth to columns.minWidth', + description: + 'Rewrites to where safe.', + pr: '#4657', +}; + +const IMPORT_SOURCES = new Set([ + '@astryxdesign/core', + '@astryxdesign/core/Grid', + '@xds/core', + '@xds/core/Grid', +]); + +/** + * @param {import('../../../../authoring/codemod/type').AstryxCodemodFile} file + * @param {import('../../../../authoring/codemod/type').CodemodTransformApi} api + * @returns {string | null | undefined} + */ +export default function transformer(file, api) { + const j = api.jscodeshift; + const root = j(file.source); + const locals = new Set(); + let hasChanges = false; + + root.find(j.ImportDeclaration).forEach((/** @type {any} */ path) => { + if (!IMPORT_SOURCES.has(path.node.source.value)) return; + for (const spec of path.node.specifiers ?? []) { + if (spec.type === 'ImportSpecifier' && spec.imported.name === 'Grid') { + locals.add(spec.local.name); + } + } + }); + if (locals.size === 0) return undefined; + + root.find(j.JSXOpeningElement).forEach((/** @type {any} */ path) => { + const name = path.node.name; + const componentName = name.type === 'JSXIdentifier' ? name.name : null; + if (!componentName || !locals.has(componentName)) return; + const attrs = path.node.attributes ?? []; + const minAttr = attrs.find( + (/** @type {any} */ a) => + a.type === 'JSXAttribute' && a.name?.name === 'minChildWidth', + ); + if (!minAttr) return; + const hasColumns = attrs.some( + (/** @type {any} */ a) => + a.type === 'JSXAttribute' && a.name?.name === 'columns', + ); + if (hasColumns) return; + + let minWidthExpr; + if ( + minAttr.value?.type === 'StringLiteral' || + minAttr.value?.type === 'Literal' + ) { + minWidthExpr = j.literal(Number(minAttr.value.value)); + } else if (minAttr.value?.type === 'JSXExpressionContainer') { + minWidthExpr = minAttr.value.expression; + } else { + return; + } + + const columnsObject = j.objectExpression([ + j.property('init', j.identifier('minWidth'), minWidthExpr), + j.property('init', j.identifier('repeat'), j.literal('fit')), + ]); + const replacement = j.jsxAttribute( + j.jsxIdentifier('columns'), + j.jsxExpressionContainer(columnsObject), + ); + attrs.splice(attrs.indexOf(minAttr), 1, replacement); + hasChanges = true; + }); + + return hasChanges ? root.toSource({quote: 'single'}) : undefined; +} diff --git a/packages/cli/assets/codemods/transforms/next/migrate-lab-codeblock-imports.mjs b/packages/cli/assets/codemods/transforms/next/migrate-lab-codeblock-imports.mjs new file mode 100644 index 000000000000..0d35cfc4c6fc --- /dev/null +++ b/packages/cli/assets/codemods/transforms/next/migrate-lab-codeblock-imports.mjs @@ -0,0 +1,52 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** + * @file Codemod: Repoint lab CodeBlock imports to core + */ + +export const meta = { + title: 'Repoint lab CodeBlock imports to core', + description: + 'Moves CodeBlock imports from @astryxdesign/lab to @astryxdesign/core/CodeBlock.', + pr: '#4657', +}; + +const CODEBLOCK_EXPORTS = new Set(['CodeBlock', 'CodeBlockProps']); + +/** + * @param {import('../../../../authoring/codemod/type').AstryxCodemodFile} file + * @param {import('../../../../authoring/codemod/type').CodemodTransformApi} api + * @returns {string | null | undefined} + */ +export default function transformer(file, api) { + const j = api.jscodeshift; + const root = j(file.source); + let hasChanges = false; + + root.find(j.ImportDeclaration).forEach((/** @type {any} */ path) => { + if (path.node.source.value !== '@astryxdesign/lab') return; + const codeBlockSpecs = []; + const remainingSpecs = []; + for (const spec of path.node.specifiers ?? []) { + if ( + spec.type === 'ImportSpecifier' && + CODEBLOCK_EXPORTS.has(spec.imported.name) + ) { + codeBlockSpecs.push(spec); + } else { + remainingSpecs.push(spec); + } + } + if (codeBlockSpecs.length === 0) return; + path.node.specifiers = remainingSpecs; + const coreImport = j.importDeclaration( + codeBlockSpecs, + j.literal('@astryxdesign/core/CodeBlock'), + ); + j(path).insertBefore(coreImport); + if (remainingSpecs.length === 0) j(path).remove(); + hasChanges = true; + }); + + return hasChanges ? root.toSource({quote: 'single'}) : undefined; +} diff --git a/packages/cli/assets/codemods/transforms/next/migrate-navmenuitem-to-navheadingmenuitem.mjs b/packages/cli/assets/codemods/transforms/next/migrate-navmenuitem-to-navheadingmenuitem.mjs new file mode 100644 index 000000000000..e3174969c2dd --- /dev/null +++ b/packages/cli/assets/codemods/transforms/next/migrate-navmenuitem-to-navheadingmenuitem.mjs @@ -0,0 +1,63 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** + * @file Codemod: Rename NavMenuItem to NavHeadingMenuItem + */ + +export const meta = { + title: 'Rename NavMenuItem to NavHeadingMenuItem', + description: + 'Rewrites the removed NavMenuItem alias and its props type to NavHeadingMenuItem.', + pr: '#4657', +}; + +const IMPORT_SOURCES = new Set([ + '@astryxdesign/core', + '@astryxdesign/core/NavMenu', + '@xds/core', + '@xds/core/NavMenu', +]); + +/** + * @param {import('../../../../authoring/codemod/type').AstryxCodemodFile} file + * @param {import('../../../../authoring/codemod/type').CodemodTransformApi} api + * @returns {string | null | undefined} + */ +export default function transformer(file, api) { + const j = api.jscodeshift; + const root = j(file.source); + const renames = new Map(); + let hasChanges = false; + + root.find(j.ImportDeclaration).forEach((/** @type {any} */ path) => { + if (!IMPORT_SOURCES.has(path.node.source.value)) return; + for (const spec of path.node.specifiers ?? []) { + if (spec.type !== 'ImportSpecifier') continue; + if ( + spec.imported.name === 'NavMenuItem' || + spec.imported.name === 'NavMenuItemProps' + ) { + const oldLocal = spec.local?.name ?? spec.imported.name; + const nextName = + spec.imported.name === 'NavMenuItem' + ? 'NavHeadingMenuItem' + : 'NavHeadingMenuItemProps'; + spec.imported.name = nextName; + spec.local = j.identifier(nextName); + renames.set(oldLocal, nextName); + hasChanges = true; + } + } + }); + + for (const [oldName, nextName] of renames) { + root + .find(j.JSXIdentifier, {name: oldName}) + .replaceWith(() => j.jsxIdentifier(nextName)); + root + .find(j.Identifier, {name: oldName}) + .replaceWith(() => j.identifier(nextName)); + } + + return hasChanges ? root.toSource({quote: 'single'}) : undefined; +} diff --git a/packages/cli/assets/codemods/transforms/next/remove-theme-transition-token-imports.mjs b/packages/cli/assets/codemods/transforms/next/remove-theme-transition-token-imports.mjs new file mode 100644 index 000000000000..7861ad687fbb --- /dev/null +++ b/packages/cli/assets/codemods/transforms/next/remove-theme-transition-token-imports.mjs @@ -0,0 +1,55 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** + * @file Codemod: Remove deprecated transition token imports + */ + +export const meta = { + title: 'Remove deprecated transition token imports', + description: + 'Removes import specifiers for transitionDefaults, transitionRaw, transitionVars, and TransitionVarName.', + pr: '#4657', +}; + +const REMOVED = new Set([ + 'transitionDefaults', + 'transitionRaw', + 'transitionVars', + 'TransitionVarName', +]); +const SOURCES = new Set([ + '@astryxdesign/core', + '@astryxdesign/core/theme', + '@astryxdesign/core/theme/tokens.stylex', + '@xds/core', + '@xds/core/theme', + '@xds/core/theme/tokens.stylex', +]); + +/** + * @param {import('../../../../authoring/codemod/type').AstryxCodemodFile} file + * @param {import('../../../../authoring/codemod/type').CodemodTransformApi} api + * @returns {string | null | undefined} + */ +export default function transformer(file, api) { + const j = api.jscodeshift; + const root = j(file.source); + let hasChanges = false; + + root.find(j.ImportDeclaration).forEach((/** @type {any} */ path) => { + if (!SOURCES.has(path.node.source.value)) return; + const before = path.node.specifiers?.length ?? 0; + path.node.specifiers = (path.node.specifiers ?? []).filter( + (/** @type {any} */ spec) => { + if (spec.type !== 'ImportSpecifier') return true; + return !REMOVED.has(spec.imported.name); + }, + ); + if ((path.node.specifiers?.length ?? 0) !== before) { + hasChanges = true; + if ((path.node.specifiers?.length ?? 0) === 0) j(path).remove(); + } + }); + + return hasChanges ? root.toSource({quote: 'single'}) : undefined; +} diff --git a/packages/cli/assets/codemods/transforms/next/rename-topnavheading-href-to-headinghref.mjs b/packages/cli/assets/codemods/transforms/next/rename-topnavheading-href-to-headinghref.mjs new file mode 100644 index 000000000000..100d94dffc27 --- /dev/null +++ b/packages/cli/assets/codemods/transforms/next/rename-topnavheading-href-to-headinghref.mjs @@ -0,0 +1,62 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** + * @file Codemod: Rename TopNavHeading href to headingHref + */ + +export const meta = { + title: 'Rename TopNavHeading href to headingHref', + description: 'Renames the removed TopNavHeading href alias to headingHref.', + pr: '#4657', +}; + +const IMPORT_SOURCES = new Set([ + '@astryxdesign/core', + '@astryxdesign/core/TopNav', + '@xds/core', + '@xds/core/TopNav', +]); + +/** + * @param {import('../../../../authoring/codemod/type').AstryxCodemodFile} file + * @param {import('../../../../authoring/codemod/type').CodemodTransformApi} api + * @returns {string | null | undefined} + */ +export default function transformer(file, api) { + const j = api.jscodeshift; + const root = j(file.source); + const locals = new Set(); + let hasChanges = false; + + root.find(j.ImportDeclaration).forEach((/** @type {any} */ path) => { + if (!IMPORT_SOURCES.has(path.node.source.value)) return; + for (const spec of path.node.specifiers ?? []) { + if ( + spec.type === 'ImportSpecifier' && + spec.imported.name === 'TopNavHeading' + ) { + locals.add(spec.local.name); + } + } + }); + if (locals.size === 0) return undefined; + + root.find(j.JSXOpeningElement).forEach((/** @type {any} */ path) => { + const name = path.node.name; + const componentName = name.type === 'JSXIdentifier' ? name.name : null; + if (!componentName || !locals.has(componentName)) return; + for (const attr of path.node.attributes ?? []) { + if (attr.type !== 'JSXAttribute') continue; + if (attr.name?.name !== 'href') continue; + const hasHeadingHref = path.node.attributes.some( + (/** @type {any} */ a) => + a.type === 'JSXAttribute' && a.name?.name === 'headingHref', + ); + if (hasHeadingHref) continue; + attr.name.name = 'headingHref'; + hasChanges = true; + } + }); + + return hasChanges ? root.toSource({quote: 'single'}) : undefined; +} diff --git a/packages/cli/assets/templates/blocks/components/TopNav/TopNavCenteredNavigation.tsx b/packages/cli/assets/templates/blocks/components/TopNav/TopNavCenteredNavigation.tsx index 9e2ea2d3e44b..da1e2a92ccd3 100644 --- a/packages/cli/assets/templates/blocks/components/TopNav/TopNavCenteredNavigation.tsx +++ b/packages/cli/assets/templates/blocks/components/TopNav/TopNavCenteredNavigation.tsx @@ -16,7 +16,7 @@ export default function TopNavCenteredNavigation() { } />} - href="#" + headingHref="#" /> } centerContent={ diff --git a/packages/cli/assets/templates/blocks/components/TopNav/TopNavEnterpriseDashboard.tsx b/packages/cli/assets/templates/blocks/components/TopNav/TopNavEnterpriseDashboard.tsx index 99d1ce45289f..073164c94155 100644 --- a/packages/cli/assets/templates/blocks/components/TopNav/TopNavEnterpriseDashboard.tsx +++ b/packages/cli/assets/templates/blocks/components/TopNav/TopNavEnterpriseDashboard.tsx @@ -21,7 +21,7 @@ export default function TopNavEnterpriseDashboard() { } />} - href="#" + headingHref="#" /> } startContent={ diff --git a/packages/cli/assets/templates/blocks/components/TopNav/TopNavHoverMenu.tsx b/packages/cli/assets/templates/blocks/components/TopNav/TopNavHoverMenu.tsx index e8e2a3759819..05950eb43d9d 100644 --- a/packages/cli/assets/templates/blocks/components/TopNav/TopNavHoverMenu.tsx +++ b/packages/cli/assets/templates/blocks/components/TopNav/TopNavHoverMenu.tsx @@ -28,7 +28,7 @@ export default function TopNavHoverMenu() { } />} - href="#" + headingHref="#" /> } startContent={ diff --git a/packages/cli/assets/templates/blocks/components/TopNav/TopNavMegaMenu.tsx b/packages/cli/assets/templates/blocks/components/TopNav/TopNavMegaMenu.tsx index bbfe4b1cdce0..933be4f5f67e 100644 --- a/packages/cli/assets/templates/blocks/components/TopNav/TopNavMegaMenu.tsx +++ b/packages/cli/assets/templates/blocks/components/TopNav/TopNavMegaMenu.tsx @@ -30,7 +30,7 @@ export default function TopNavMegaMenuBlock() { } />} - href="#" + headingHref="#" /> } startContent={ diff --git a/packages/cli/assets/templates/blocks/components/TopNav/TopNavMultipleDropdowns.tsx b/packages/cli/assets/templates/blocks/components/TopNav/TopNavMultipleDropdowns.tsx index 910634abfb54..3bd759738db0 100644 --- a/packages/cli/assets/templates/blocks/components/TopNav/TopNavMultipleDropdowns.tsx +++ b/packages/cli/assets/templates/blocks/components/TopNav/TopNavMultipleDropdowns.tsx @@ -24,7 +24,7 @@ export default function TopNavMultipleDropdowns() { } />} - href="#" + headingHref="#" /> } startContent={ diff --git a/packages/cli/assets/templates/blocks/components/TopNav/TopNavWithLogo.tsx b/packages/cli/assets/templates/blocks/components/TopNav/TopNavWithLogo.tsx index 8b737c83eb39..2078dc808d4c 100644 --- a/packages/cli/assets/templates/blocks/components/TopNav/TopNavWithLogo.tsx +++ b/packages/cli/assets/templates/blocks/components/TopNav/TopNavWithLogo.tsx @@ -16,7 +16,7 @@ export default function TopNavWithLogo() { } />} - href="#" + headingHref="#" /> } startContent={ diff --git a/packages/cli/assets/templates/blocks/components/TopNavHeading/TopNavHeadingShowcase.tsx b/packages/cli/assets/templates/blocks/components/TopNavHeading/TopNavHeadingShowcase.tsx index c2daaf1bebb4..e3017248b0db 100644 --- a/packages/cli/assets/templates/blocks/components/TopNavHeading/TopNavHeadingShowcase.tsx +++ b/packages/cli/assets/templates/blocks/components/TopNavHeading/TopNavHeadingShowcase.tsx @@ -39,7 +39,11 @@ export default function TopNavHeadingShowcase() { } href="/" /> + } + headingHref="/" + /> } /> diff --git a/packages/cli/assets/templates/pages/shell-top-nav/page.tsx b/packages/cli/assets/templates/pages/shell-top-nav/page.tsx index df7a28e30dd8..4cc8304611e4 100644 --- a/packages/cli/assets/templates/pages/shell-top-nav/page.tsx +++ b/packages/cli/assets/templates/pages/shell-top-nav/page.tsx @@ -146,7 +146,7 @@ export default function ShellTopNav() { logo={ } /> } - href="#" + headingHref="#" /> } centerContent={ @@ -208,7 +208,7 @@ export default function ShellTopNav() { {[0, 1, 2].map(section => ( - + {CATEGORY_TILES.map(tile => ( diff --git a/packages/cli/assets/templates/themes/neutral/neutralTheme.ts b/packages/cli/assets/templates/themes/neutral/neutralTheme.ts index e63208148881..ace0fd91ecf7 100644 --- a/packages/cli/assets/templates/themes/neutral/neutralTheme.ts +++ b/packages/cli/assets/templates/themes/neutral/neutralTheme.ts @@ -144,7 +144,10 @@ export const neutralTheme = defineTheme({ // Text '--color-text-primary': ['#171717', '#fafafa'], - '--color-text-secondary': ['#737373', '#a3a3a3'], + // Light secondary is neutral-600 (#525252), not 500 (#737373): 500 only + // reaches 4.19:1 on the T95 body (#f1f1f1), just under WCAG AA 4.5:1. + // 600 clears it (6.9:1 on body, 7.8:1 on card). Dark stays neutral-400. + '--color-text-secondary': ['#525252', '#a3a3a3'], '--color-text-disabled': ['#a3a3a3', '#525252'], '--color-text-accent': ['#262626', '#ebebeb'], '--color-on-dark': '#ffffff', @@ -482,6 +485,37 @@ export const neutralTheme = defineTheme({ }, }, + // ========================================================================= + // StatusDot — fill uses the SAME vivid stops as the filled semantic Badge + // (and ProgressBar), so a dot and its badge read as one status language. + // + // The default component maps each variant to a raw semantic token + // (--color-success / --color-error / --color-warning / --color-icon- + // secondary), which in light mode are the dark T30/T40 stops meant to + // sit as TEXT on a pastel surface — as a solid dot they read muddy + // (dark green / maroon / brown). Redirect them to the badge fills. + // + // success → badge success bg (green T45 / dark-ramp T60) + // warning → badge warning bg (yellow T85, same hex both modes) + // error → badge error bg (red T55 / dark-ramp T60) + // accent → badge info bg (blue T50 / dark-ramp T60) — the + // StatusDot "accent" is the info/attention color, so it + // pairs with the info badge rather than --color-accent + // (near-black #262626, the darkest offender). + // + // `neutral` is intentionally NOT overridden: the neutral badge bg is a + // near-invisible light gray (--color-background-gray #e5e5e5 / 10% white + // wash), fine as a large pill but unreadable as an 8px dot. It keeps the + // component default's visible mid-gray (--color-icon-secondary), which is + // not among the "too dark" cases. + // ========================================================================= + statusdot: { + 'variant:success': {backgroundColor: 'light-dark(#198100, #64af4c)'}, + 'variant:warning': {backgroundColor: '#ffce2f'}, + 'variant:error': {backgroundColor: 'light-dark(#e33f4a, #ff705d)'}, + 'variant:accent': {backgroundColor: 'light-dark(#0074e2, #6d9cfe)'}, + }, + // ========================================================================= // Banner — sits on a hue-tinted surface with colored text/icon: // Light: pastel T90 bg (pulled from --color-{X}-muted / --color-background-blue) diff --git a/packages/cli/clients/cli/commands/component.test.mjs b/packages/cli/clients/cli/commands/component.test.mjs index 9fb692fec43a..65e8189956df 100644 --- a/packages/cli/clients/cli/commands/component.test.mjs +++ b/packages/cli/clients/cli/commands/component.test.mjs @@ -6,7 +6,6 @@ import * as path from 'node:path'; import * as os from 'node:os'; import { discoverComponents, - discoverExternalComponents, discoverExternalComponentsGrouped, findExternalComponentDoc, findComponentReadme, @@ -387,71 +386,6 @@ describe('findClosestComponents', () => { }); }); -describe('discoverExternalComponents', () => { - it('finds .doc.mjs files in the docs directory', () => { - const docsDir = path.join(tmpDir, 'src'); - const compDir = path.join(docsDir, 'Employee'); - fs.mkdirSync(compDir, {recursive: true}); - fs.writeFileSync(path.join(compDir, 'EmployeeHoverCard.doc.mjs'), ''); - fs.writeFileSync(path.join(compDir, 'EmployeeLink.doc.mjs'), ''); - - const result = discoverExternalComponents(docsDir); - expect(result).toEqual(['EmployeeHoverCard', 'EmployeeLink']); - }); - - it('scans nested directories recursively', () => { - const docsDir = path.join(tmpDir, 'src'); - const deepDir = path.join(docsDir, 'a', 'b', 'c'); - fs.mkdirSync(deepDir, {recursive: true}); - fs.writeFileSync(path.join(deepDir, 'DeepComponent.doc.mjs'), ''); - - const result = discoverExternalComponents(docsDir); - expect(result).toEqual(['DeepComponent']); - }); - - it('ignores non-.doc.mjs files', () => { - const docsDir = path.join(tmpDir, 'src'); - fs.mkdirSync(docsDir, {recursive: true}); - fs.writeFileSync(path.join(docsDir, 'Foo.doc.mjs'), ''); - fs.writeFileSync(path.join(docsDir, 'Foo.tsx'), ''); - fs.writeFileSync(path.join(docsDir, 'README.md'), ''); - fs.writeFileSync(path.join(docsDir, 'index.mjs'), ''); - - const result = discoverExternalComponents(docsDir); - expect(result).toEqual(['Foo']); - }); - - it('skips node_modules and __tests__ directories', () => { - const docsDir = path.join(tmpDir, 'src'); - const nmDir = path.join(docsDir, 'node_modules', 'dep'); - const testDir = path.join(docsDir, '__tests__'); - fs.mkdirSync(nmDir, {recursive: true}); - fs.mkdirSync(testDir, {recursive: true}); - fs.writeFileSync(path.join(nmDir, 'Hidden.doc.mjs'), ''); - fs.writeFileSync(path.join(testDir, 'TestOnly.doc.mjs'), ''); - fs.writeFileSync(path.join(docsDir, 'Visible.doc.mjs'), ''); - - const result = discoverExternalComponents(docsDir); - expect(result).toEqual(['Visible']); - }); - - it('returns empty array for nonexistent directory', () => { - const result = discoverExternalComponents(path.join(tmpDir, 'nope')); - expect(result).toEqual([]); - }); - - it('returns sorted results', () => { - const docsDir = path.join(tmpDir, 'src'); - fs.mkdirSync(docsDir, {recursive: true}); - fs.writeFileSync(path.join(docsDir, 'Zebra.doc.mjs'), ''); - fs.writeFileSync(path.join(docsDir, 'Alpha.doc.mjs'), ''); - fs.writeFileSync(path.join(docsDir, 'Middle.doc.mjs'), ''); - - const result = discoverExternalComponents(docsDir); - expect(result).toEqual(['Alpha', 'Middle', 'Zebra']); - }); -}); - describe('discoverExternalComponentsGrouped', () => { it('reads group: from doc files and groups components', () => { const docsDir = path.join(tmpDir, 'src'); diff --git a/packages/cli/clients/cli/commands/component/index.mjs b/packages/cli/clients/cli/commands/component/index.mjs index a447136bbfdb..fa727a7697f3 100644 --- a/packages/cli/clients/cli/commands/component/index.mjs +++ b/packages/cli/clients/cli/commands/component/index.mjs @@ -280,7 +280,7 @@ export function registerComponent(program) { // Re-export lib functions for backward compatibility // (agent-docs.mjs, tests, and generate-skill-doc.sh import from here) -export {discoverComponents, discoverExternalComponents, discoverExternalComponentsGrouped, findComponentReadme, findComponentSource, findExternalComponentDoc, resolveImportPath} from '../../../../foundation/discovery/component-discovery.mjs'; +export {discoverComponents, discoverExternalComponentsGrouped, findComponentReadme, findComponentSource, findExternalComponentDoc, resolveImportPath} from '../../../../foundation/discovery/component-discovery.mjs'; export {discoverExternalPackages} from '../../../../foundation/fs/paths.mjs'; export {loadDocs} from '../../../../foundation/discovery/component-loader.mjs'; export {formatFull, formatCompact, formatBrief, formatProps, formatBriefAll} from '../../lib/component-format.mjs'; diff --git a/packages/cli/foundation/discovery/component-discovery.mjs b/packages/cli/foundation/discovery/component-discovery.mjs index 886c4cc79af1..caf99d8e8009 100644 --- a/packages/cli/foundation/discovery/component-discovery.mjs +++ b/packages/cli/foundation/discovery/component-discovery.mjs @@ -396,37 +396,6 @@ export function resolveImportPath(coreDir, componentName) { // ── External package discovery ─────────────────────────────────────── -/** - * Discover components from an external package's docs directory. - * Scans for *.doc.mjs files and returns their names as a flat array. - * - * @deprecated Use discoverExternalComponentsGrouped for group-aware discovery. - * @param {string} docsDir - * @returns {string[]} - */ -export function discoverExternalComponents(docsDir) { - if (!fs.existsSync(docsDir)) return []; - /** @type {string[]} */ - const components = []; - - /** @param {string} dirPath */ - function scanDir(dirPath) { - const entries = fs.readdirSync(dirPath, {withFileTypes: true}); - for (const entry of entries) { - if (entry.name === 'node_modules' || entry.name === '__tests__') continue; - const fullPath = path.join(dirPath, entry.name); - if (entry.isDirectory()) { - scanDir(fullPath); - } else if (entry.name.endsWith('.doc.mjs')) { - components.push(entry.name.replace('.doc.mjs', '')); - } - } - } - - scanDir(docsDir); - return components.sort(); -} - /** * Discover components from an external package's docs directory, * reading `group:` fields from each .doc.mjs for subcategories. diff --git a/packages/cli/foundation/discovery/component-discovery.test.mjs b/packages/cli/foundation/discovery/component-discovery.test.mjs index a87a58d4bb3c..cbe7c9f1034c 100644 --- a/packages/cli/foundation/discovery/component-discovery.test.mjs +++ b/packages/cli/foundation/discovery/component-discovery.test.mjs @@ -19,7 +19,6 @@ import { findComponentReadme, findComponentSource, resolveImportPath, - discoverExternalComponents, discoverExternalComponentsGrouped, findExternalComponentDoc, discoverIntegrationComponents, @@ -172,9 +171,6 @@ describe('external package discovery', () => { return docs; } - it('discoverExternalComponents returns a sorted flat list (deprecated)', () => { - expect(discoverExternalComponents(buildExternalDocs())).toEqual(['AppShell', 'Diff', 'Secret', 'SideNav']); - }); it('discoverExternalComponentsGrouped groups + drops hidden', () => { expect(discoverExternalComponentsGrouped(buildExternalDocs())).toEqual({ @@ -190,7 +186,6 @@ describe('external package discovery', () => { }); it('returns empty for a missing docs dir (guarded)', () => { - expect(discoverExternalComponents('/no/such/dir')).toEqual([]); expect(discoverExternalComponentsGrouped('/no/such/dir')).toEqual({}); expect(findExternalComponentDoc('/no/such/dir', 'X')).toBeNull(); }); diff --git a/packages/core/src/Calendar/Calendar.test.tsx b/packages/core/src/Calendar/Calendar.test.tsx index 51de968c42d7..9eb83c7d6e29 100644 --- a/packages/core/src/Calendar/Calendar.test.tsx +++ b/packages/core/src/Calendar/Calendar.test.tsx @@ -19,9 +19,13 @@ import type {CalendarHandle} from './Calendar'; import type {ISODateString} from './Calendar'; import {calendarStyles} from './styles'; import {defineTheme} from '../theme/defineTheme'; -import {generateThemeCSSFlat} from '../theme/generateThemeRules'; +import {generateThemeCSS} from '../theme/generateThemeRules'; import {__resetLiveRegionsForTest} from '../hooks/useAnnounce'; +function generateThemeTestCSS(theme: Parameters[0]) { + const {prose, component} = generateThemeCSS(theme); + return [prose, component].filter(Boolean).join('\n\n'); +} afterEach(() => { __resetLiveRegionsForTest(); }); @@ -1148,7 +1152,7 @@ describe('Calendar', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-calendar-day.today-only'); expect(css).toContain('.astryx-calendar-day.today-in-range'); expect(css).toContain('box-shadow: inset 0 0 0 2px var(--color-accent)'); @@ -1229,7 +1233,7 @@ describe('Calendar', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-calendar-nav {'); expect(css).toContain('color: var(--color-accent)'); expect(css).toContain('.astryx-calendar-nav.next'); diff --git a/packages/core/src/Calendar/index.ts b/packages/core/src/Calendar/index.ts index 41c95d653ad0..9182200b7261 100644 --- a/packages/core/src/Calendar/index.ts +++ b/packages/core/src/Calendar/index.ts @@ -38,11 +38,6 @@ export type { } from './hooks'; // Re-export calendar-specific utilities for advanced usage -export { - isSameDay, - isDateInRange, - getWeekNumber, - formatAccessibleDate, -} from './utils'; +export {isSameDay, isDateInRange, getWeekNumber} from './utils'; // Re-export theme styles for customization diff --git a/packages/core/src/Calendar/utils.ts b/packages/core/src/Calendar/utils.ts index d3c06abd0313..ff14b8d3e06a 100644 --- a/packages/core/src/Calendar/utils.ts +++ b/packages/core/src/Calendar/utils.ts @@ -7,8 +7,6 @@ * @position Shared utilities; used by Calendar, CalendarMonthGrid, CalendarDayCell */ -import {plainDateFormat, DATE_FORMAT_WITH_WEEKDAY} from '../utils/plainDate'; - export { type PlainDate, plainDateFromISO as parseISO, @@ -17,12 +15,3 @@ export { plainDateIsInRange as isDateInRange, plainDateGetWeekNumber as getWeekNumber, } from '../utils/plainDate'; - -export {plainDateFormat, DATE_FORMAT_WITH_WEEKDAY}; - -/** @deprecated Use `plainDateFormat(pd, DATE_FORMAT_WITH_WEEKDAY)` instead. */ -export function formatAccessibleDate( - ...args: Parameters -): string { - return plainDateFormat(args[0], DATE_FORMAT_WITH_WEEKDAY); -} diff --git a/packages/core/src/CommandPalette/CommandPaletteGroup.test.tsx b/packages/core/src/CommandPalette/CommandPaletteGroup.test.tsx index 0f0af6cd7071..913db8dae422 100644 --- a/packages/core/src/CommandPalette/CommandPaletteGroup.test.tsx +++ b/packages/core/src/CommandPalette/CommandPaletteGroup.test.tsx @@ -10,8 +10,12 @@ import {describe, it, expect} from 'vitest'; import {render, screen} from '@testing-library/react'; import {CommandPaletteGroup} from './CommandPaletteGroup'; import {defineTheme} from '../theme/defineTheme'; -import {generateThemeCSSFlat} from '../theme/generateThemeRules'; +import {generateThemeCSS} from '../theme/generateThemeRules'; +function generateThemeTestCSS(theme: Parameters[0]) { + const {prose, component} = generateThemeCSS(theme); + return [prose, component].filter(Boolean).join('\n\n'); +} describe('CommandPaletteGroup', () => { it('renders heading', () => { render( @@ -116,7 +120,7 @@ describe('CommandPaletteGroup', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-command-palette-group-heading {'); expect(css).toContain('padding-block: var(--spacing-2)'); expect(css).toContain('font-weight: var(--font-weight-bold)'); diff --git a/packages/core/src/DateInput/DateInput.test.tsx b/packages/core/src/DateInput/DateInput.test.tsx index ef2ce89236cb..88d40c00a225 100644 --- a/packages/core/src/DateInput/DateInput.test.tsx +++ b/packages/core/src/DateInput/DateInput.test.tsx @@ -24,8 +24,12 @@ import {Icon} from '../Icon'; import {InputGroup} from '../InputGroup'; import {InputGroupText} from '../InputGroup/InputGroupText'; import {defineTheme} from '../theme/defineTheme'; -import {generateThemeCSSFlat} from '../theme/generateThemeRules'; +import {generateThemeCSS} from '../theme/generateThemeRules'; +function generateThemeTestCSS(theme: Parameters[0]) { + const {prose, component} = generateThemeCSS(theme); + return [prose, component].filter(Boolean).join('\n\n'); +} describe('DateInput', () => { it('renders with label', () => { render( {}} />); @@ -1073,7 +1077,7 @@ describe('DateInput clear icon theme target', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-date-input-clear-icon {'); expect(css).toContain('width: 12px'); expect(css).toContain('height: 12px'); @@ -1165,7 +1169,7 @@ describe('DateInput calendar-toggle icon theme target', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-date-input-toggle-icon {'); expect(css).toContain('width: 14px'); expect(css).toContain('height: 14px'); diff --git a/packages/core/src/DateRangeInput/DateRangeInput.test.tsx b/packages/core/src/DateRangeInput/DateRangeInput.test.tsx index e6baa64d1d08..f3436f62547d 100644 --- a/packages/core/src/DateRangeInput/DateRangeInput.test.tsx +++ b/packages/core/src/DateRangeInput/DateRangeInput.test.tsx @@ -12,6 +12,11 @@ import {describe, it, expect, vi, beforeEach} from 'vitest'; import {render, screen, fireEvent, waitFor} from '@testing-library/react'; import userEvent from '@testing-library/user-event'; + +function generateThemeTestCSS(theme: Parameters[0]) { + const {prose, component} = generateThemeCSS(theme); + return [prose, component].filter(Boolean).join('\n\n'); +} // getButton/queryButton instead of getByRole('button', {name}): the closed // popover keeps a two-month Calendar (~85 role=button nodes) mounted, which // made every role+name query compute ~85 accessible names through jsdom's @@ -21,7 +26,7 @@ import {DateRangeInput} from './DateRangeInput'; import type {DateRange} from './DateRangeInput'; import {Icon} from '../Icon'; import {defineTheme} from '../theme/defineTheme'; -import {generateThemeCSSFlat} from '../theme/generateThemeRules'; +import {generateThemeCSS} from '../theme/generateThemeRules'; describe('DateRangeInput', () => { it('renders with label', () => { @@ -603,7 +608,7 @@ describe('DateRangeInput icon theme targets', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-date-range-input-clear-icon'); expect(css).toContain('.astryx-date-range-input-toggle-icon'); expect(css).toContain(':hover'); diff --git a/packages/core/src/DateTimeInput/DateTimeInput.test.tsx b/packages/core/src/DateTimeInput/DateTimeInput.test.tsx index 91589c4ac267..098026f73193 100644 --- a/packages/core/src/DateTimeInput/DateTimeInput.test.tsx +++ b/packages/core/src/DateTimeInput/DateTimeInput.test.tsx @@ -16,8 +16,12 @@ import {getButton, queryButton} from '../__tests__/fastRoleQueries'; import {DateTimeInput} from './DateTimeInput'; import type {ISODateTimeString} from './DateTimeInput'; import {defineTheme} from '../theme/defineTheme'; -import {generateThemeCSSFlat} from '../theme/generateThemeRules'; +import {generateThemeCSS} from '../theme/generateThemeRules'; +function generateThemeTestCSS(theme: Parameters[0]) { + const {prose, component} = generateThemeCSS(theme); + return [prose, component].filter(Boolean).join('\n\n'); +} describe('DateTimeInput', () => { it('renders with label', () => { render( {}} />); @@ -917,7 +921,7 @@ describe('DateTimeInput', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-date-time-input-date-segment {'); expect(css).toContain('.astryx-date-time-input-date-segment.lg'); diff --git a/packages/core/src/Dialog/Dialog.doc.mjs b/packages/core/src/Dialog/Dialog.doc.mjs index cd49804eed13..ce8b65dad813 100644 --- a/packages/core/src/Dialog/Dialog.doc.mjs +++ b/packages/core/src/Dialog/Dialog.doc.mjs @@ -76,9 +76,7 @@ export const docs = { type: 'DialogPosition', description: 'Static position for the dialog; centered by default when omitted. ' + - 'Prefer logical `start`/`end` (they map correctly under RTL) over the ' + - 'deprecated physical `left`/`right`, which do not mirror. Logical ' + - 'wins if both are set.', + 'Use logical `start`/`end` for inline offsets so positioned dialogs mirror correctly under RTL.', }, { name: 'variant', diff --git a/packages/core/src/Dialog/Dialog.test.tsx b/packages/core/src/Dialog/Dialog.test.tsx index 484c9fd9d906..e78910a832ac 100644 --- a/packages/core/src/Dialog/Dialog.test.tsx +++ b/packages/core/src/Dialog/Dialog.test.tsx @@ -190,7 +190,7 @@ describe('Dialog', () => { {}} - position={{top: 100, right: 20}}> + position={{top: 100, end: 20}}> Content , ); @@ -202,7 +202,7 @@ describe('Dialog', () => { {}} - position={{top: '10vh', left: '5vw'}}> + position={{top: '10vh', start: '5vw'}}> Content , ); @@ -235,61 +235,21 @@ describe('Dialog', () => { expect(offsets.insetInlineStart).toBe('20px'); expect(offsets.insetInlineEnd).toBe('40px'); // No physical offsets requested → auto. - expect(offsets.left).toBe('auto'); - expect(offsets.right).toBe('auto'); - }); - - it('keeps physical left/right physical — they do NOT mirror', () => { - // Physical props are the visual-left / visual-right edge in BOTH LTR and - // RTL (unchanged from pre-deprecation). - const offsets = resolveDialogPositionOffsets({left: 20, right: 40}); - expect(offsets.left).toBe('20px'); - expect(offsets.right).toBe('40px'); - // insetInlineStart/End default to auto (no logical positioning requested). - expect(offsets.insetInlineStart).toBe('auto'); - expect(offsets.insetInlineEnd).toBe('auto'); }); it('combines block-axis top/bottom with an inline pair', () => { const offsets = resolveDialogPositionOffsets({top: 100, start: 12}); expect(offsets.top).toBe('100px'); expect(offsets.insetInlineStart).toBe('12px'); - // Everything unset falls back to auto — no mirroring for a logical-only - // consumer's physical slots. + // Everything unset falls back to auto. expect(offsets.bottom).toBe('auto'); - expect(offsets.left).toBe('auto'); - expect(offsets.right).toBe('auto'); expect(offsets.insetInlineEnd).toBe('auto'); }); - it('is non-breaking: physical-only offsets match pre-deprecation output', () => { - const offsets = resolveDialogPositionOffsets({top: 100, right: 20}); - expect(offsets.top).toBe('100px'); - expect(offsets.right).toBe('20px'); - expect(offsets.left).toBe('auto'); - expect(offsets.bottom).toBe('auto'); - expect(offsets.insetInlineStart).toBe('auto'); - expect(offsets.insetInlineEnd).toBe('auto'); - }); - - it('passes through string offsets (vw/vh/etc.) for both APIs', () => { + it('passes through string offsets (vw/vh/etc.) for logical offsets', () => { const logical = resolveDialogPositionOffsets({start: '5vw', end: '10%'}); expect(logical.insetInlineStart).toBe('5vw'); expect(logical.insetInlineEnd).toBe('10%'); - - const physical = resolveDialogPositionOffsets({left: '5vw', top: '10vh'}); - expect(physical.left).toBe('5vw'); - expect(physical.top).toBe('10vh'); - }); - - it('forbids mixing logical and physical inline offsets at compile time', () => { - // The union type makes a mixed inline pair a type error — a single dialog - // cannot be positioned both logically and physically. These would not - // compile (kept as documentation of the enforced contract): - // @ts-expect-error start (logical) cannot combine with left (physical) - resolveDialogPositionOffsets({start: 5, left: 99}); - // @ts-expect-error end (logical) cannot combine with right (physical) - resolveDialogPositionOffsets({end: 7, right: 88}); }); }); diff --git a/packages/core/src/Dialog/Dialog.tsx b/packages/core/src/Dialog/Dialog.tsx index 55affcc9fb57..fe9ccc42be97 100644 --- a/packages/core/src/Dialog/Dialog.tsx +++ b/packages/core/src/Dialog/Dialog.tsx @@ -97,27 +97,14 @@ interface DialogBlockPosition { * Static position for a dialog. The inline axis is logical XOR physical — the * type forbids mixing the two, so a single dialog can't be positioned both ways: * - Logical `start`/`end` map to `inset-inline-*` and mirror under RTL (preferred). - * - Physical `left`/`right` are deprecated, do not mirror, and are removed in a - * future major. * Block-axis `top`/`bottom` may be combined with either. */ -export type DialogPosition = - | (DialogBlockPosition & { - /** Logical inline-start offset (`inset-inline-start`); mirrors under RTL. */ - start?: number | string; - /** Logical inline-end offset (`inset-inline-end`); mirrors under RTL. */ - end?: number | string; - left?: never; - right?: never; - }) - | (DialogBlockPosition & { - /** @deprecated Use `start`. Physical (never mirrors); removed in a future major. */ - left?: number | string; - /** @deprecated Use `end`. Physical (never mirrors); removed in a future major. */ - right?: number | string; - start?: never; - end?: never; - }); +export interface DialogPosition extends DialogBlockPosition { + /** Logical inline-start offset (`inset-inline-start`); mirrors under RTL. */ + start?: number | string; + /** Logical inline-end offset (`inset-inline-end`); mirrors under RTL. */ + end?: number | string; +} const enterDirectional = stylex.keyframes({ from: { @@ -223,9 +210,7 @@ const dynamicStyles = stylex.create({ top: string, insetInlineStart: string, insetInlineEnd: string, - right: string, bottom: string, - left: string, ) => ({ // Assigns pre-resolved offsets from resolveDialogPositionOffsets(). This // literal has no logic — StyleX can't analyze a helper, so the values @@ -235,11 +220,7 @@ const dynamicStyles = stylex.create({ top, insetInlineStart, insetInlineEnd, - // eslint-disable-next-line @astryx/no-physical-properties -- deprecated consumer-facing DialogPosition.right; physical by contract, superseded by logical `end` - right, bottom, - // eslint-disable-next-line @astryx/no-physical-properties -- deprecated consumer-facing DialogPosition.left; physical by contract, superseded by logical `start` - left, }), }); @@ -252,9 +233,8 @@ function formatPosition(value: number | string): string { /** * Map a {@link DialogPosition} to resolved CSS offsets. Logical `start`/`end` - * become `inset-inline-*` (mirror under RTL); physical `left`/`right` stay - * physical. The union type guarantees at most one inline pair is set, so no - * precedence is needed — each unset offset falls back to `auto`. + * become `inset-inline-*` (mirror under RTL); each unset offset falls back to + * `auto`. * * Not re-exported from the package; internal to Dialog. Directly unit-tested * so the mapping is verified without StyleX class compilation. @@ -262,7 +242,7 @@ function formatPosition(value: number | string): string { * @see DialogPosition */ export function resolveDialogPositionOffsets(position: DialogPosition) { - const {top, bottom, start, end, left, right} = position; + const {top, bottom, start, end} = position; return { top: top !== undefined ? formatPosition(top) : 'auto', @@ -270,9 +250,6 @@ export function resolveDialogPositionOffsets(position: DialogPosition) { // Logical offsets mirror under RTL (preferred replacements). insetInlineStart: start !== undefined ? formatPosition(start) : 'auto', insetInlineEnd: end !== undefined ? formatPosition(end) : 'auto', - // Deprecated physical offsets never mirror. - left: left !== undefined ? formatPosition(left) : 'auto', - right: right !== undefined ? formatPosition(right) : 'auto', }; } @@ -675,9 +652,7 @@ export function Dialog({ o.top, o.insetInlineStart, o.insetInlineEnd, - o.right, o.bottom, - o.left, ); })(), isFullscreen && styles.fullscreen, diff --git a/packages/core/src/Grid/Grid.doc.mjs b/packages/core/src/Grid/Grid.doc.mjs index b520a8682b3b..c64bb982dc03 100644 --- a/packages/core/src/Grid/Grid.doc.mjs +++ b/packages/core/src/Grid/Grid.doc.mjs @@ -33,11 +33,6 @@ export const docs = { type: "number | {minWidth: number, max?: number, repeat?: 'fill' | 'fit'}", description: 'Column configuration. Use a number for fixed columns (e.g. `columns={3}`). Use an object for responsive columns: `minWidth` sets the minimum column width in px, `repeat` controls track behavior (`"fill"` preserves empty tracks for consistent widths, `"fit"` collapses empty tracks so items stretch; defaults to `"fill"`), and `max` caps the maximum number of columns.', }, - { - name: 'minChildWidth', - type: 'number', - description: 'Deprecated: use `columns={{minWidth: 280}}` instead. Minimum item width in px; enables responsive auto-fit.', - }, { name: 'width', type: 'SizeValue', diff --git a/packages/core/src/Grid/Grid.tsx b/packages/core/src/Grid/Grid.tsx index 20da4e9067c3..5e153a737737 100644 --- a/packages/core/src/Grid/Grid.tsx +++ b/packages/core/src/Grid/Grid.tsx @@ -63,15 +63,6 @@ export interface GridProps extends BaseProps { */ columns?: GridColumns; - /** - * Minimum width of each grid item in pixels. - * Enables responsive auto-fit behavior. - * - * @deprecated Use `columns={{minWidth: 280}}` instead. - * @default 0 (disabled) - */ - minChildWidth?: number; - /** * Width of the grid container. * Numbers are treated as pixels, strings are used as-is (e.g., '100%'). @@ -384,7 +375,6 @@ function buildCappedTemplate( */ export function Grid({ columns, - minChildWidth = 0, rowHeight, width, height, @@ -421,20 +411,6 @@ export function Grid({ } else { gridTemplateColumns = `repeat(${repeatMode}, minmax(${columns.minWidth}px, 1fr))`; } - } else if (minChildWidth > 0) { - // Deprecated path: minChildWidth uses auto-fit for backward compat - const numColumns = typeof columns === 'number' ? columns : 0; - if (numColumns > 0) { - gridTemplateColumns = buildCappedTemplate( - minChildWidth, - numColumns, - 'auto-fit', - gap, - columnGap, - ); - } else { - gridTemplateColumns = `repeat(auto-fit, minmax(${minChildWidth}px, 1fr))`; - } } else if (typeof columns === 'number' && columns > 0) { // Fixed columns mode gridTemplateColumns = `repeat(${columns}, 1fr)`; diff --git a/packages/core/src/MultiSelector/MultiSelector.test.tsx b/packages/core/src/MultiSelector/MultiSelector.test.tsx index 4f90b9760034..f2608862c1b9 100644 --- a/packages/core/src/MultiSelector/MultiSelector.test.tsx +++ b/packages/core/src/MultiSelector/MultiSelector.test.tsx @@ -22,8 +22,12 @@ import {MultiSelector} from './MultiSelector'; import {Icon} from '../Icon'; import {__resetLiveRegionsForTest} from '../hooks/useAnnounce'; import {defineTheme} from '../theme/defineTheme'; -import {generateThemeCSSFlat} from '../theme/generateThemeRules'; +import {generateThemeCSS} from '../theme/generateThemeRules'; +function generateThemeTestCSS(theme: Parameters[0]) { + const {prose, component} = generateThemeCSS(theme); + return [prose, component].filter(Boolean).join('\n\n'); +} // Module-level constants to satisfy @eslint-react/no-unstable-default-props. const ANNOUNCE_OPTIONS = ['Apple', 'Banana', 'Orange'] as const; const EMPTY_VALUE: string[] = []; @@ -1560,7 +1564,7 @@ describe('MultiSelector clear icon theme target', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-multi-selector-clear-icon {'); expect(css).toContain('width: 12px'); expect(css).toContain('height: 12px'); @@ -1673,7 +1677,7 @@ describe('MultiSelector indicator (chevron) icon theme target', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-multi-selector-indicator-icon {'); expect(css).toContain('width: 14px'); expect(css).toContain('height: 14px'); diff --git a/packages/core/src/NavMenu/NavHeadingMenu.test.tsx b/packages/core/src/NavMenu/NavHeadingMenu.test.tsx index e2b3cf54b064..b52eb7207958 100644 --- a/packages/core/src/NavMenu/NavHeadingMenu.test.tsx +++ b/packages/core/src/NavMenu/NavHeadingMenu.test.tsx @@ -5,7 +5,6 @@ import {render, screen, fireEvent} from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import {NavHeadingMenu} from './NavHeadingMenu'; import {NavHeadingMenuItem} from './NavHeadingMenuItem'; -import {NavMenuItem} from './NavMenuItem'; import {NavHeadingCloseContext} from './NavMenuContext'; describe('NavHeadingMenu', () => { @@ -322,18 +321,3 @@ describe('context forwarding', () => { expect(closeMenu).toHaveBeenCalledOnce(); }); }); - -describe('NavMenuItem backward compat', () => { - it('is the same component as NavHeadingMenuItem', () => { - expect(NavMenuItem).toBe(NavHeadingMenuItem); - }); - - it('renders correctly when used as NavMenuItem', () => { - render( - - - , - ); - expect(screen.getByRole('menuitem')).toHaveTextContent('Legacy'); - }); -}); diff --git a/packages/core/src/NavMenu/NavMenuItem.tsx b/packages/core/src/NavMenu/NavMenuItem.tsx deleted file mode 100644 index 9063b92e8cb1..000000000000 --- a/packages/core/src/NavMenu/NavMenuItem.tsx +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Meta Platforms, Inc. and affiliates. - -'use client'; - -/** - * @file NavMenuItem.tsx - * @input NavHeadingMenuItem - * @output Exports deprecated NavMenuItem alias - * @position Compatibility re-export; prefer NavHeadingMenuItem - */ - -import {NavHeadingMenuItem} from './NavHeadingMenuItem'; -import type {NavHeadingMenuItemProps} from './NavHeadingMenuItem'; - -/** @deprecated Use NavHeadingMenuItem instead. */ -export type NavMenuItemProps = NavHeadingMenuItemProps; - -/** @deprecated Use NavHeadingMenuItem instead. */ -export const NavMenuItem = NavHeadingMenuItem; diff --git a/packages/core/src/NavMenu/index.ts b/packages/core/src/NavMenu/index.ts index ae46ba886fa9..799deddfb659 100644 --- a/packages/core/src/NavMenu/index.ts +++ b/packages/core/src/NavMenu/index.ts @@ -16,7 +16,3 @@ export type { NavHeadingCloseContextValue, NavHeadingMenuSize, } from './NavMenuContext'; - -// Backward compat — use NavHeadingMenuItem instead. -export {NavMenuItem} from './NavMenuItem'; -export type {NavMenuItemProps} from './NavMenuItem'; diff --git a/packages/core/src/Selector/Selector.test.tsx b/packages/core/src/Selector/Selector.test.tsx index b14f246929a0..c59683dde462 100644 --- a/packages/core/src/Selector/Selector.test.tsx +++ b/packages/core/src/Selector/Selector.test.tsx @@ -24,8 +24,12 @@ import {Icon} from '../Icon'; import {InputGroup, InputGroupText} from '../InputGroup'; import {__resetLiveRegionsForTest} from '../hooks/useAnnounce'; import {defineTheme} from '../theme/defineTheme'; -import {generateThemeCSSFlat} from '../theme/generateThemeRules'; +import {generateThemeCSS} from '../theme/generateThemeRules'; +function generateThemeTestCSS(theme: Parameters[0]) { + const {prose, component} = generateThemeCSS(theme); + return [prose, component].filter(Boolean).join('\n\n'); +} function politeRegion(): HTMLElement | null { return document.querySelector('[data-astryx-live-region="polite"]'); } @@ -1371,7 +1375,7 @@ describe('Selector clear icon theme target', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-selector-clear-icon {'); expect(css).toContain('width: 12px'); expect(css).toContain('height: 12px'); @@ -1465,7 +1469,7 @@ describe('Selector indicator (chevron) icon theme target', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-selector-indicator-icon {'); expect(css).toContain('width: 14px'); expect(css).toContain('height: 14px'); diff --git a/packages/core/src/Switch/Switch.doc.mjs b/packages/core/src/Switch/Switch.doc.mjs index 6be6eb55c921..17107b180531 100644 --- a/packages/core/src/Switch/Switch.doc.mjs +++ b/packages/core/src/Switch/Switch.doc.mjs @@ -141,7 +141,7 @@ export const docs = { name: 'labelSpacing', type: "'hug' | 'spread'", description: - 'Spacing behavior between label and switch. "hug" places them next to each other; "spread" pushes them to opposite ends of the container (full width). "default" is a deprecated alias for "hug".', + 'Spacing behavior between label and switch. "hug" places them next to each other; "spread" pushes them to opposite ends of the container (full width).', default: "'hug'", }, { @@ -429,6 +429,6 @@ export const docsDense = { labelTooltip: 'Tooltip text in info icon at label end.', labelPosition: 'Which side label appears; "start" places before switch.', labelSpacing: - 'Spacing behavior; "hug" places next to each other, "spread" pushes to opposite ends (full width). "default" is deprecated alias for "hug".', + 'Spacing behavior; "hug" places next to each other, "spread" pushes to opposite ends (full width).', }, }; diff --git a/packages/core/src/Switch/Switch.test.tsx b/packages/core/src/Switch/Switch.test.tsx index 161fd7a96c4f..03954248803c 100644 --- a/packages/core/src/Switch/Switch.test.tsx +++ b/packages/core/src/Switch/Switch.test.tsx @@ -619,31 +619,6 @@ describe('Switch', () => { expect(getField(explicit)).not.toHaveAttribute('data-label-spacing'); expect(getField(explicit).className).toBe(getField(implicit).className); }); - - it("treats the deprecated 'default' value as an alias for hug", () => { - const {container: hug} = render( - {}} - labelSpacing="hug" - />, - ); - const {container: deprecated} = render( - {}} - labelSpacing="default" - />, - ); - expect(getField(deprecated)).not.toHaveAttribute('data-label-spacing'); - expect(getField(deprecated).className).toBe(getField(hug).className); - // The switch row keeps the hug layout, not the spread justification. - expect(getField(deprecated).firstElementChild?.className).toBe( - getField(hug).firstElementChild?.className, - ); - }); }); describe('form participation', () => { it('submits under htmlName when on', () => { diff --git a/packages/core/src/Switch/Switch.tsx b/packages/core/src/Switch/Switch.tsx index 2e0b4fac8cf4..9d0716a5d30b 100644 --- a/packages/core/src/Switch/Switch.tsx +++ b/packages/core/src/Switch/Switch.tsx @@ -284,11 +284,7 @@ const styles = stylex.create({ export type SwitchLabelPosition = 'start' | 'end'; -export type SwitchLabelSpacing = - | 'hug' - | 'spread' - /** @deprecated Use `'hug'` instead. */ - | 'default'; +export type SwitchLabelSpacing = 'hug' | 'spread'; export interface SwitchProps extends Omit { /** Ref forwarded to the root element */ @@ -402,8 +398,6 @@ export interface SwitchProps extends Omit { * Spacing behavior between label and switch. * - 'hug': Label and switch are positioned next to each other * - 'spread': Label and switch are pushed to opposite ends - * - * 'default' is a deprecated alias for 'hug'. * @default 'hug' */ labelSpacing?: SwitchLabelSpacing; @@ -482,10 +476,6 @@ export function Switch({ const isOn = optimisticValue === true; - // 'default' is a deprecated alias for 'hug' (#2889). - const resolvedLabelSpacing: SwitchLabelSpacing = - labelSpacing === 'default' ? 'hug' : labelSpacing; - // Disabled-reason tooltip. Disabled controls swallow pointer events, so the // tooltip listeners attach to the switch row (which already exists) and the // native checkbox stays perceivable via aria-disabled instead of the disabled @@ -617,8 +607,7 @@ export function Switch({ {...mergeProps( themeProps('switch-field', { labelPosition: labelPosition !== 'end' ? labelPosition : undefined, - labelSpacing: - resolvedLabelSpacing !== 'hug' ? resolvedLabelSpacing : undefined, + labelSpacing: labelSpacing !== 'hug' ? labelSpacing : undefined, }), stylex.props(width != null && dynamicWidthStyles.width(width), xstyle), className, @@ -637,7 +626,7 @@ export function Switch({ }} {...stylex.props( styles.container, - resolvedLabelSpacing === 'spread' && styles.containerSpread, + labelSpacing === 'spread' && styles.containerSpread, !isDisabled && switchScope, )}> {' '} diff --git a/packages/core/src/Table/BaseTable.tsx b/packages/core/src/Table/BaseTable.tsx index 43cb126f537a..fb18469f5c01 100644 --- a/packages/core/src/Table/BaseTable.tsx +++ b/packages/core/src/Table/BaseTable.tsx @@ -331,7 +331,6 @@ function BaseTableInner>({ idKey, plugins: pluginsProp, children, - tableProps: userTableProps, textOverflow = 'wrap', scrollWrapper: ScrollWrapper, emptyState, @@ -394,7 +393,7 @@ function BaseTableInner>({ // --- Plugin pipeline: table --- const tableRenderProps = applyPlugins(plugins, p => p.transformTable, { - htmlProps: {...userTableProps}, + htmlProps: {}, xstyle: children ? [styles.table, styles.tableAutoLayout] : [styles.table], } satisfies TableRenderProps); @@ -498,8 +497,8 @@ function BaseTableInner>({ const hasData = data != null && data.length > 0; const hasColumns = resolvedColumns.length > 0; - // Style precedence: deprecated tableProps.style < consumer style < the - // computed column min-width (structural — derived from column defs, so it + // Style precedence: consumer style < the computed column min-width + // (structural — derived from column defs, so it // must win when present; when absent, a consumer minWidth survives). const tableStyle: React.CSSProperties = { ...tableRenderProps.htmlProps.style, diff --git a/packages/core/src/Table/Table.test.tsx b/packages/core/src/Table/Table.test.tsx index 01a78727486c..9d2eb01dfcac 100644 --- a/packages/core/src/Table/Table.test.tsx +++ b/packages/core/src/Table/Table.test.tsx @@ -401,20 +401,6 @@ describe('BaseTable', () => { expect(ref).toHaveBeenCalledWith(expect.any(HTMLTableElement)); }); - it('passes tableProps to the table element', () => { - render( - , - ); - expect(screen.getByRole('table')).toHaveAttribute( - 'aria-label', - 'Users table', - ); - }); - describe('root element styling props (#3679)', () => { it('applies className to the table element', () => { render(); @@ -446,27 +432,6 @@ describe('BaseTable', () => { expect(table).toHaveAttribute('data-analytics', 'tables'); }); - it('composes with deprecated tableProps, direct props winning conflicts', () => { - render( -
, - ); - const table = screen.getByRole('table'); - expect(table.className).toContain('legacy'); - expect(table.className).toContain('direct'); - // Direct style wins the conflicting key; non-conflicting legacy survives. - expect(table.style.opacity).toBe('1'); - expect(table.style.color).toBe('red'); - }); - it('keeps the computed column min-width over a consumer style.minWidth', () => { const {tableMinWidth} = resolveColumnWidths(columns); render( @@ -483,20 +448,6 @@ describe('BaseTable', () => { expect(screen.getByRole('table').style.minWidth).toBe('10px'); }); - it('direct id and aria attributes beat the same keys in tableProps', () => { - render( -
, - ); - const table = screen.getByRole('table', {name: 'Direct'}); - expect(table.id).toBe('direct-id'); - }); - it('keeps the astryx theme classes alongside a consumer className', () => { render(
); const table = screen.getByRole('table'); diff --git a/packages/core/src/Table/index.ts b/packages/core/src/Table/index.ts index b5efeee87d33..9f9f731121fb 100644 --- a/packages/core/src/Table/index.ts +++ b/packages/core/src/Table/index.ts @@ -28,11 +28,11 @@ export {useTableColumnResize} from './plugins/columnResize'; export {useTableStickyColumns} from './plugins/stickyColumns'; export {useTableGroupedRows} from './plugins/groupedRows'; export {useTableRowIndex} from './plugins/rowIndex'; -export {useTableRowStatus} from './plugins/rowStatus'; export { useTableRowExpansion, useTableRowExpansionState, } from './plugins/rowExpansion'; +export {useTableRowStatus} from './plugins/rowStatus'; export {useTableTreeData, useTableTreeState} from './plugins/tree'; export {resolveContextActions} from './tableContextMenu'; export { diff --git a/packages/core/src/Table/plugins/groupedRows/useTableGroupedRows.tsx b/packages/core/src/Table/plugins/groupedRows/useTableGroupedRows.tsx index 4ec1b0a18235..f7dd5f182558 100644 --- a/packages/core/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +++ b/packages/core/src/Table/plugins/groupedRows/useTableGroupedRows.tsx @@ -177,7 +177,7 @@ const styles = stylex.create({ * toggle, the group label, and a member count; collapsing hides that group's * data rows while keeping the header visible. * - * Mirrors {@link useTableRowExpansionState}: the consumer owns the + * Mirrors other controlled Table state helpers: the consumer owns the * `collapsedGroups` set and this hook returns `{data, plugin, idKey}` — * pass all three to `
`. * diff --git a/packages/core/src/Table/types.ts b/packages/core/src/Table/types.ts index 35217769087e..fef4dc884a8f 100644 --- a/packages/core/src/Table/types.ts +++ b/packages/core/src/Table/types.ts @@ -575,15 +575,6 @@ export interface BaseTableProps< /** Children mode — render ``/`
` directly instead of data-driven */ children?: ReactNode; - /** - * Additional HTML attributes for the `` element. - * - * @deprecated Pass `className`, `style`, `xstyle`, and other HTML - * attributes directly on the component instead — they now reach the root - * `
` and win over `tableProps` on conflicts. Migrate with - * `npx astryx upgrade --codemod migrate-table-tableprops-to-direct-props`. - */ - tableProps?: HTMLAttributes; /** * Optional wrapper rendered around the `
` element, inside the * plugin `transformTableContext` layer. Used by `Table` to add a diff --git a/packages/core/src/TopNav/TopNav.test.tsx b/packages/core/src/TopNav/TopNav.test.tsx index 75fb34a07611..034c2c064b0f 100644 --- a/packages/core/src/TopNav/TopNav.test.tsx +++ b/packages/core/src/TopNav/TopNav.test.tsx @@ -281,8 +281,8 @@ describe('TopNavHeading', () => { }); }); - it('renders as anchor when href is provided', () => { - render(); + it('renders as anchor when headingHref is provided', () => { + render(); const link = screen.getByRole('link'); expect(link).toHaveAttribute('href', '/'); }); diff --git a/packages/core/src/TopNav/TopNavHeading.doc.mjs b/packages/core/src/TopNav/TopNavHeading.doc.mjs index dcdf89684471..f349878a03a9 100644 --- a/packages/core/src/TopNav/TopNavHeading.doc.mjs +++ b/packages/core/src/TopNav/TopNavHeading.doc.mjs @@ -33,11 +33,6 @@ export const docs = { type: 'string', description: 'Link for the heading text (e.g. product home). When no menu is present and this is the only href, the whole heading becomes one clickable link.', }, - { - name: 'href', - type: 'string', - description: 'Deprecated: use headingHref instead. URL to navigate to when clicked.', - }, { name: 'superheading', type: 'string', @@ -122,11 +117,6 @@ export const docsZh = { type: 'string', description: '标题文本的链接(如产品首页)。当没有菜单且这是唯一的 href 时,整个标题变为一个可点击链接。', }, - { - name: 'href', - type: 'string', - description: '已弃用,请使用 headingHref。点击时导航到的 URL。', - }, { name: 'superheading', type: 'string', @@ -179,7 +169,6 @@ export const docsDense = { logo: 'Logo before heading text. Image, NavIcon, or ReactNode.', heading: 'Product/app name.', headingHref: 'Link for heading (product home). Only href + no menu → whole heading is link.', - href: 'Deprecated: use headingHref.', superheading: 'Text above heading (suite name). Smaller secondary style.', superheadingHref: 'Link for superheading. Independent inline link when menu present.', subheading: 'Text below heading (account context). Smaller secondary style.', diff --git a/packages/core/src/TopNav/TopNavHeading.tsx b/packages/core/src/TopNav/TopNavHeading.tsx index d97e88c24c4a..e00328315c36 100644 --- a/packages/core/src/TopNav/TopNavHeading.tsx +++ b/packages/core/src/TopNav/TopNavHeading.tsx @@ -227,14 +227,8 @@ export interface TopNavHeadingProps extends BaseProps { heading?: string; /** * Link for the heading (e.g., product home). - * Alias: `href` (for backward compatibility). */ headingHref?: string; - /** - * @deprecated Use `headingHref` instead. - * URL to navigate to when the heading is clicked. - */ - href?: string; /** * Text above the heading (e.g., suite name). */ @@ -304,8 +298,7 @@ export function TopNavHeading({ logo, logoLabel, heading, - headingHref: headingHrefProp, - href, + headingHref, superheading, superheadingHref, subheading, @@ -322,8 +315,6 @@ export function TopNavHeading({ const t = useTranslator(); const chevronDownIcon = useIcon('chevronDown'); const LinkComponent = useLinkComponent(as); - // Support both headingHref and legacy href - const headingHref = headingHrefProp ?? href; // When the logo is wrapped in a link it needs its own accessible name (the // logo image itself is decorative). Prefer an explicit logoLabel, fall back // to the heading text. axe: link-name. diff --git a/packages/core/src/TreeList/TreeList.test.tsx b/packages/core/src/TreeList/TreeList.test.tsx index 991e903c866f..f1317dcc390d 100644 --- a/packages/core/src/TreeList/TreeList.test.tsx +++ b/packages/core/src/TreeList/TreeList.test.tsx @@ -15,8 +15,12 @@ import userEvent from '@testing-library/user-event'; import {TreeList} from './TreeList'; import type {TreeListItemData} from './TreeListTypes'; import {defineTheme} from '../theme/defineTheme'; -import {generateThemeCSSFlat} from '../theme/generateThemeRules'; +import {generateThemeCSS} from '../theme/generateThemeRules'; +function generateThemeTestCSS(theme: Parameters[0]) { + const {prose, component} = generateThemeCSS(theme); + return [prose, component].filter(Boolean).join('\n\n'); +} const simpleItems: TreeListItemData[] = [ {id: 'a', label: 'Item A'}, {id: 'b', label: 'Item B'}, @@ -485,7 +489,7 @@ describe('TreeList', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-tree-list-guide {'); expect(css).toContain('background-color: var(--color-accent)'); }); @@ -501,7 +505,7 @@ describe('TreeList', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-tree-list-guide {'); expect(css).toContain('display: none'); }); @@ -525,7 +529,7 @@ describe('TreeList', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-tree-list {'); expect(css).toContain('--tree-list-indent: var(--spacing-5)'); }); @@ -609,7 +613,7 @@ describe('TreeList', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-tree-list-chevron {'); expect(css).toContain('color: var(--color-accent)'); expect(css).toContain('.astryx-tree-list-chevron.expanded'); @@ -667,7 +671,7 @@ describe('TreeList', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-tree-list-item-label {'); expect(css).toContain('color: var(--color-text-primary)'); expect(css).toContain('.astryx-tree-list-item-label.selected'); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 8e2b78c1ffe5..40b9962883b7 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -173,24 +173,3 @@ export * from './theme'; // Internationalization export * from './i18n'; - -// Doc types — for external library authors writing .doc.mjs files. -// @deprecated Import these from `@astryxdesign/cli/authoring` instead. These -// re-exports are kept for one release cycle and will be removed; `astryx upgrade` -// repoints them automatically. -export type { - ComponentDoc, - SingleComponentDoc, - MultiComponentDoc, - ComponentPropDoc, - ComponentEntry, - ComponentThemingTarget, - ComponentThemingVar, - ComponentThemingDerivedVar, - ComponentTranslationDoc, - ComponentGroupDoc, - ReferenceDoc, - ReferenceSection, - ReferenceContentBlock, - ReferenceTokenPreviewType, -} from '@astryxdesign/cli/authoring'; diff --git a/packages/core/src/theme/defineTheme.test.ts b/packages/core/src/theme/defineTheme.test.ts index 8004818af6c7..75540401ea80 100644 --- a/packages/core/src/theme/defineTheme.test.ts +++ b/packages/core/src/theme/defineTheme.test.ts @@ -2,13 +2,12 @@ import {describe, it, expect, vi} from 'vitest'; import type {IconRegistry} from '../Icon/globalIconRegistry'; -import { - defineTheme, - generateThemeCSS, - generateThemeCSSFlat, - isDefinedTheme, -} from './defineTheme'; +import {defineTheme, generateThemeCSS, isDefinedTheme} from './defineTheme'; +function generateThemeTestCSS(theme: Parameters[0]) { + const {prose, component} = generateThemeCSS(theme); + return [prose, component].filter(Boolean).join('\n\n'); +} describe('defineTheme', () => { it('creates a theme with name', () => { const theme = defineTheme({name: 'test'}); @@ -100,7 +99,7 @@ describe('generateThemeCSS', () => { '--radius-container': '16px', }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('@scope'); expect(css).toContain('--color-accent: light-dark(#0077B6, #48CAE4)'); expect(css).toContain('--radius-container: 16px'); @@ -110,7 +109,7 @@ describe('generateThemeCSS', () => { it('includes prose rules even with no overrides', () => { const theme = defineTheme({name: 'empty'}); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('@scope'); expect(css).toContain(':where(h1, h2, h3, h4, h5, h6)'); expect(css).toContain('font-family: var(--font-family-heading)'); @@ -192,7 +191,7 @@ describe('generateThemeCSS with components', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-card {'); expect(css).toContain('border-width: 2px'); expect(css).toContain('border-color: var(--color-accent)'); @@ -211,7 +210,7 @@ describe('generateThemeCSS with components', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-button.secondary'); expect(css).toContain('background-color: rgba(0,0,0,0.06)'); }); @@ -227,7 +226,7 @@ describe('generateThemeCSS with components', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-button.destructive.sm'); expect(css).toContain('padding: 2px 6px'); }); @@ -241,7 +240,7 @@ describe('generateThemeCSS with components', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('font-family: "Playfair Display", serif'); expect(css).not.toContain('fontFamily'); }); @@ -254,7 +253,7 @@ describe('generateThemeCSS with components', () => { card: {base: {borderWidth: '1px'}}, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('@scope'); expect(css).toContain('--radius-container: 20px'); expect(css).toContain('.astryx-card {'); @@ -488,7 +487,7 @@ describe('custom status via components', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); // parseStyleKey('status:neutral') → '.neutral', so CSS should have .astryx-banner.neutral expect(css).toContain('.astryx-banner.neutral'); expect(css).toContain('background-color: var(--color-background-muted)'); @@ -521,7 +520,7 @@ describe('custom status via components', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-button.primary-muted'); expect(css).toContain('background-color: #ECF5FF'); }); @@ -733,7 +732,7 @@ describe('pseudo-class overrides in components', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); // Base rule expect(css).toContain('.astryx-radio {'); expect(css).toContain('border-color: #8F9296'); @@ -761,7 +760,7 @@ describe('pseudo-class overrides in components', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-button.primary-muted {'); expect(css).toContain('background-color: #ECF5FF'); expect(css).toContain('.astryx-button.primary-muted:hover {'); @@ -783,7 +782,7 @@ describe('pseudo-class overrides in components', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); // Should NOT emit an empty base rule expect(css).not.toMatch(/\.astryx-switch\s*\{\s*\}/); // Should emit the pseudo rule @@ -802,7 +801,7 @@ describe('pseudo-class overrides in components', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('.astryx-card {'); expect(css).toContain('border-width: 2px'); expect(css).toContain('border-color: var(--color-accent)'); @@ -821,7 +820,7 @@ describe('container padding mapping', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); // Should NOT emit raw padding property (only the scoped token) expect(css).not.toMatch(/[^-]padding: 20px/); // Should emit component-scoped shorthand token @@ -840,7 +839,7 @@ describe('container padding mapping', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('--astryx-card-padding-inline: 20px'); expect(css).toContain('--astryx-card-padding-block-start: 16px'); expect(css).toContain('--astryx-card-padding-block-end: 16px'); @@ -857,7 +856,7 @@ describe('container padding mapping', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('--astryx-card-padding-inline: 16px'); expect(css).toContain('--astryx-card-padding-block-start: 24px'); expect(css).toContain('--astryx-card-padding-block-end: 24px'); @@ -875,7 +874,7 @@ describe('container padding mapping', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); // Section — uniform → shorthand expect(css).toContain('--astryx-section-padding: 12px'); // Dialog — asymmetric → directional @@ -892,7 +891,7 @@ describe('container padding mapping', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); // Button is not a container — padding passes through as-is expect(css).toContain('padding: 8px 16px'); expect(css).not.toContain('--astryx-button-padding'); @@ -911,7 +910,7 @@ describe('container padding mapping', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); // Non-padding props pass through expect(css).toContain('--_card-radius: 16px'); expect(css).toContain('background-color: white'); @@ -929,7 +928,7 @@ describe('container padding mapping', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('--astryx-card-padding-block-start: 16px'); expect(css).toContain('--astryx-card-padding-block-end: 12px'); expect(css).toContain('--astryx-card-padding-inline: 20px'); @@ -944,7 +943,7 @@ describe('container padding mapping', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('--astryx-card-padding-block-start: 16px'); expect(css).toContain('--astryx-card-padding-block-end: 24px'); }); @@ -958,7 +957,7 @@ describe('container padding mapping', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('--astryx-card-padding-inline-start: 12px'); expect(css).toContain('--astryx-card-padding-inline-end: 20px'); }); @@ -972,7 +971,7 @@ describe('container padding mapping', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('--astryx-card-padding-block-start: 32px'); expect(css).not.toContain('--astryx-card-padding-block-end'); expect(css).not.toContain('--astryx-card-padding-inline'); @@ -987,7 +986,7 @@ describe('container padding mapping', () => { }, }, }); - const css = generateThemeCSSFlat(theme); + const css = generateThemeTestCSS(theme); expect(css).toContain('--astryx-card-padding-inline-start: 8px'); expect(css).toContain('--astryx-card-padding-inline-end: 24px'); }); diff --git a/packages/core/src/theme/defineTheme.ts b/packages/core/src/theme/defineTheme.ts index f0c713c32977..08c19f477fff 100644 --- a/packages/core/src/theme/defineTheme.ts +++ b/packages/core/src/theme/defineTheme.ts @@ -45,7 +45,6 @@ import { shadowDefaults, durationDefaults, easeDefaults, - transitionDefaults, typographyDefaults, textSizeDefaults, fontWeightDefaults, @@ -78,7 +77,6 @@ export type CoreTokenName = | keyof typeof shadowDefaults | keyof typeof durationDefaults | keyof typeof easeDefaults - | keyof typeof transitionDefaults | keyof typeof typographyDefaults | keyof typeof textSizeDefaults | keyof typeof fontWeightDefaults @@ -367,7 +365,6 @@ export const tokenDefaults: Record = { ...shadowDefaults, ...durationDefaults, ...easeDefaults, - ...transitionDefaults, ...typographyDefaults, ...textSizeDefaults, ...fontWeightDefaults, @@ -648,7 +645,6 @@ export { generateThemeRulesSplit, generateOnMediaCSS, generateThemeCSS, - generateThemeCSSFlat, type ThemeRulesSplit, type ThemeCSSOutput, } from './generateThemeRules'; diff --git a/packages/core/src/theme/generateThemeRules.ts b/packages/core/src/theme/generateThemeRules.ts index 0fef62eed258..8bbeaea93501 100644 --- a/packages/core/src/theme/generateThemeRules.ts +++ b/packages/core/src/theme/generateThemeRules.ts @@ -708,18 +708,3 @@ export function generateThemeCSS(theme: DefinedTheme): ThemeCSSOutput { return {prose: proseCss, component: componentCss}; } - -/** - * Generate the full CSS string for a theme as a single string. - * @deprecated Use generateThemeCSS() which returns { prose, component } for proper layering. - * This flat version is kept for backwards compatibility with tests and simple cases. - */ -export function generateThemeCSSFlat(theme: DefinedTheme): string { - const rules = generateThemeRules(theme); - if (rules.length === 0) { - return ''; - } - const scopeSelector = themeScopeStart(theme.name); - const inner = rules.join('\n\n'); - return `@scope (${scopeSelector}) to (${THEME_SCOPE_TO}) {\n${inner}\n}`; -} diff --git a/packages/core/src/theme/index.ts b/packages/core/src/theme/index.ts index e2230431f0bf..dda4fce9f37a 100644 --- a/packages/core/src/theme/index.ts +++ b/packages/core/src/theme/index.ts @@ -21,7 +21,6 @@ export type {MediaThemeProps} from './MediaTheme'; export { defineTheme, generateThemeCSS, - generateThemeCSSFlat, generateOnMediaCSS, generateThemeRules, generateThemeRulesSplit, @@ -95,7 +94,6 @@ export { shadowDefaults, durationDefaults, easeDefaults, - transitionDefaults, typographyDefaults, textSizeDefaults, fontWeightDefaults, @@ -108,7 +106,6 @@ export { shadowVars, durationVars, easeVars, - transitionVars, typographyVars, textSizeVars, fontWeightVars, @@ -125,7 +122,6 @@ export type { ShadowVarName, DurationVarName, EaseVarName, - TransitionVarName, TypographyVarName, TextSizeVarName, FontWeightVarName, diff --git a/packages/core/src/theme/tokens.stylex.ts b/packages/core/src/theme/tokens.stylex.ts index 53c71202b25f..9db606ac29d3 100644 --- a/packages/core/src/theme/tokens.stylex.ts +++ b/packages/core/src/theme/tokens.stylex.ts @@ -272,23 +272,6 @@ export const easeDefaults = { export const easeVars = stylex.defineVars(easeDefaults); export type EaseVarName = keyof typeof easeDefaults; - -// ============================================================================= -// Motion Tokens — Deprecated (transition shorthand) -// ============================================================================= - -/** @deprecated Use durationVars + easeVars instead */ -export const transitionDefaults = { - '--transition-fast': '0.15s ease', - '--transition-normal': '0.2s ease', -} as const; - -/** @deprecated Use durationVars + easeVars instead */ -export const transitionRaw = transitionDefaults; - -/** @deprecated Use durationVars + easeVars instead */ -export const transitionVars = stylex.defineVars(transitionDefaults); - // ============================================================================= // Typography Tokens - Font Families // ============================================================================= @@ -347,8 +330,6 @@ export type SpacingVarName = keyof typeof spacingDefaults; export type SizeVarName = keyof typeof sizeDefaults; export type RadiusVarName = keyof typeof radiusDefaults; export type ShadowVarName = keyof typeof shadowDefaults; -/** @deprecated Use DurationVarName | EaseVarName instead */ -export type TransitionVarName = keyof typeof transitionDefaults; export type TypographyVarName = keyof typeof typographyDefaults; export type TextSizeVarName = keyof typeof textSizeDefaults; export type FontWeightVarName = keyof typeof fontWeightDefaults; diff --git a/packages/lab/src/CodeBlock/index.ts b/packages/lab/src/CodeBlock/index.ts deleted file mode 100644 index befa9a955286..000000000000 --- a/packages/lab/src/CodeBlock/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Meta Platforms, Inc. and affiliates. - -/** - * @file CodeBlock component barrel export - * @deprecated Import from '@astryxdesign/core/CodeBlock' instead. - * This re-export exists for backwards compatibility. - */ - -export {CodeBlock} from '@astryxdesign/core/CodeBlock'; -export type {CodeBlockProps} from '@astryxdesign/core/CodeBlock'; diff --git a/packages/lab/src/index.ts b/packages/lab/src/index.ts index afe37523c132..81905fa95678 100644 --- a/packages/lab/src/index.ts +++ b/packages/lab/src/index.ts @@ -13,7 +13,6 @@ */ // Code components — syntax highlighting domain -export {CodeBlock, type CodeBlockProps} from './CodeBlock'; export {CodeEditor, type CodeEditorProps} from './CodeEditor'; export { tokenize,