diff --git a/.changeset/promote-drawer-from-lab-to-core-with-shared-laye-6m5v.md b/.changeset/promote-drawer-from-lab-to-core-with-shared-laye-6m5v.md new file mode 100644 index 0000000000000..52d7eac87a877 --- /dev/null +++ b/.changeset/promote-drawer-from-lab-to-core-with-shared-laye-6m5v.md @@ -0,0 +1,7 @@ +--- +'@astryxdesign/cli': minor +'@astryxdesign/core': minor +--- + +[breaking] Promote Drawer from `@astryxdesign/lab` to `@astryxdesign/core/Drawer`; run `astryx upgrade --apply` to migrate imports. +@imdreamrunner diff --git a/.github/scripts/modal-close-visibility.js b/.github/scripts/modal-close-visibility.js index 5ac42ef90152a..b3785cc89b0e2 100644 --- a/.github/scripts/modal-close-visibility.js +++ b/.github/scripts/modal-close-visibility.js @@ -47,7 +47,7 @@ const TARGETS = [ }, { component: 'Drawer (modal)', - story: 'lab-drawer--showcase', + story: 'core-drawer--showcase', openButton: 'Open inspector', // Reproduces the real failure condition: after the native host releases the // top layer, this becomes the containing block for the fixed panel. @@ -56,7 +56,7 @@ const TARGETS = [ }, { component: 'Drawer (non-modal)', - story: 'lab-drawer--row-inspector', + story: 'core-drawer--row-inspector', openButton: 'web-01 / us-east-1', host: 'popover', transformAncestor: true, @@ -65,7 +65,7 @@ const TARGETS = [ }, { component: 'Drawer (stacked exit)', - story: 'lab-drawer--stacked-drawers', + story: 'core-drawer--stacked-drawers', openButton: 'Open order', nestedButton: 'Open line item', host: 'popover', diff --git a/.github/scripts/visual-gate/lib/plan.test.mjs b/.github/scripts/visual-gate/lib/plan.test.mjs index 5d8d112ecb40d..d42e29adfbbb0 100644 --- a/.github/scripts/visual-gate/lib/plan.test.mjs +++ b/.github/scripts/visual-gate/lib/plan.test.mjs @@ -53,7 +53,7 @@ const themeOverrides = { describe('storiesInPackages', () => { const mixed = [ ...stories, - story({id: 'lab-drawer--default', title: 'Lab/Drawer', name: 'Default', component: 'Drawer'}, '@astryxdesign/lab', false), + story({id: 'lab-stat--default', title: 'Lab/Stat', name: 'Default', component: 'Stat'}, '@astryxdesign/lab', false), story({id: 'charts-bar--default', title: 'Charts/Bar', name: 'Default', component: 'Bar'}, '@astryxdesign/charts', false), ]; @@ -110,10 +110,10 @@ describe('canonicalBaselineStories', () => { it('drops a canary component even when its story is titled under a canonical group', () => { const labInCore = story( { - id: 'core-drawer--default', - title: 'Core/Drawer', + id: 'core-stat--default', + title: 'Core/Stat', name: 'Default', - component: 'Drawer', + component: 'Stat', componentPackage: '@astryxdesign/lab', }, '@astryxdesign/lab', diff --git a/.github/scripts/visual-scope.test.mjs b/.github/scripts/visual-scope.test.mjs index 70e7a441ceb95..0b71dbd6795b8 100644 --- a/.github/scripts/visual-scope.test.mjs +++ b/.github/scripts/visual-scope.test.mjs @@ -167,7 +167,7 @@ describe('classifyVisualScope', () => { it('excludes Lab and records its release channel from package metadata', () => { const result = classifyVisualScope( - ['packages/lab/src/Drawer/Drawer.tsx'], + ['packages/lab/src/Stat/Stat.tsx'], root, ); expect(result.hasStableVisual).toBe(false); @@ -190,7 +190,7 @@ describe('classifyVisualScope', () => { execFileSync( process.execPath, [SCRIPT, '--manifests', manifests, '--github-output', output], - {input: 'packages/lab/src/Drawer/Drawer.tsx\n'}, + {input: 'packages/lab/src/Stat/Stat.tsx\n'}, ), ).toThrow(); }); diff --git a/apps/sandbox/src/app/(sandbox)/pages/component-scores/page.tsx b/apps/sandbox/src/app/(sandbox)/pages/component-scores/page.tsx index 26ac2bb80c662..73fd7547ddfb9 100644 --- a/apps/sandbox/src/app/(sandbox)/pages/component-scores/page.tsx +++ b/apps/sandbox/src/app/(sandbox)/pages/component-scores/page.tsx @@ -37,7 +37,8 @@ import { radiusVars, spacingVars, } from '@astryxdesign/core/theme/tokens.stylex'; -import {Drawer, Stat} from '@astryxdesign/lab'; +import {Drawer} from '@astryxdesign/core/Drawer'; +import {Stat} from '@astryxdesign/lab'; import { AUDIT_PROMPT, diff --git a/apps/sandbox/src/app/(sandbox)/pages/motion-lab/__generated__/motionAudit.ts b/apps/sandbox/src/app/(sandbox)/pages/motion-lab/__generated__/motionAudit.ts index db4a34a82bb20..52b5008fdb283 100644 --- a/apps/sandbox/src/app/(sandbox)/pages/motion-lab/__generated__/motionAudit.ts +++ b/apps/sandbox/src/app/(sandbox)/pages/motion-lab/__generated__/motionAudit.ts @@ -31,9 +31,9 @@ export const CORE_VERSION = '0.5.2'; export const AUDIT_COUNTS = { componentDirs: 121, - coreComponentDirs: 104, - labComponentDirs: 17, - hardcodedInLab: 10, + coreComponentDirs: 105, + labComponentDirs: 16, + hardcodedInLab: 8, filesScanned: 721, animatingComponents: 56, staticComponents: 64, @@ -405,20 +405,20 @@ export const HARDCODED_SITES: ReadonlyArray = [ ms: null, }, { - file: 'lab/Drawer/Drawer.tsx', - package: 'lab', + file: 'core/Drawer/Drawer.tsx', + package: 'core', component: 'Drawer', - line: 121, + line: 110, kind: 'duration', prop: 'transitionDuration', value: '0.01s', ms: 10, }, { - file: 'lab/Drawer/Drawer.tsx', - package: 'lab', + file: 'core/Drawer/Drawer.tsx', + package: 'core', component: 'Drawer', - line: 194, + line: 183, kind: 'duration', prop: 'transitionDuration', value: '0.01s', @@ -682,6 +682,13 @@ export const DURATION_WITHOUT_CURVE: ReadonlyArray = [ line: 111, decl: "transitionDuration: durationVars['--duration-fast']", }, + { + file: 'core/Drawer/Drawer.tsx', + package: 'core', + component: 'Drawer', + line: 181, + decl: "transitionDuration: '0.01s'", + }, { file: 'core/MobileNav/MobileNav.tsx', package: 'core', @@ -724,13 +731,6 @@ export const DURATION_WITHOUT_CURVE: ReadonlyArray = [ line: 107, decl: "transitionDuration: '150ms'", }, - { - file: 'lab/Drawer/Drawer.tsx', - package: 'lab', - component: 'Drawer', - line: 192, - decl: "transitionDuration: '0.01s'", - }, ]; export const TRANSFORM_TRANSITIONS: ReadonlyArray = [ @@ -804,6 +804,13 @@ export const TRANSFORM_TRANSITIONS: ReadonlyArray = [ line: 371, decl: "transitionProperty: 'transform'", }, + { + file: 'core/Drawer/Drawer.tsx', + package: 'core', + component: 'Drawer', + line: 106, + decl: "transitionProperty: 'transform'", + }, { file: 'core/Lightbox/Lightbox.tsx', package: 'core', @@ -965,13 +972,6 @@ export const TRANSFORM_TRANSITIONS: ReadonlyArray = [ line: 219, decl: "transitionProperty: 'transform'", }, - { - file: 'lab/Drawer/Drawer.tsx', - package: 'lab', - component: 'Drawer', - line: 117, - decl: "transitionProperty: 'transform'", - }, ]; export const TRANSITION_ALL: ReadonlyArray = []; @@ -1233,11 +1233,11 @@ export const HARDCODED_BY_COMPONENT: ReadonlyArray = ['BottomSheet', 6], ['(shared)', 4], ['CommandPalette', 2], + ['Drawer', 2], ['MobileNav', 2], ['StatusDot', 2], ['ChatReasoning', 2], ['CircularProgress', 2], - ['Drawer', 2], ['Button', 1], ['Lightbox', 1], ['ProgressBar', 1], diff --git a/apps/sandbox/src/app/(sandbox)/pages/motion-lab/bugs/page.tsx b/apps/sandbox/src/app/(sandbox)/pages/motion-lab/bugs/page.tsx index 28be4654e853d..f506fcb134ef2 100644 --- a/apps/sandbox/src/app/(sandbox)/pages/motion-lab/bugs/page.tsx +++ b/apps/sandbox/src/app/(sandbox)/pages/motion-lab/bugs/page.tsx @@ -564,7 +564,9 @@ export default function MotionBugsPage() { }> + badges={ + + }> This was the lab failure. The former implementation @@ -574,12 +576,12 @@ export default function MotionBugsPage() { slide never rendered. - Lab Drawer now waits for the actual transform transition and uses a + Core Drawer now waits for the actual transform transition and uses a computed-duration backstop in{' '} - lab/Drawer/useDrawerDialogPresence.ts. Theme motion can - no longer desynchronize the visual exit from native-host release; - the sliders below preserve the original failure as an interactive - explanation of why the fix matters. + core/Drawer/useDrawerDialogPresence.ts. Theme motion + can no longer desynchronize the visual exit from native-host + release; the sliders below preserve the original failure as an + interactive explanation of why the fix matters. diff --git a/apps/sandbox/src/app/(sandbox)/templates/page.tsx b/apps/sandbox/src/app/(sandbox)/templates/page.tsx index ee8ec51180111..cc3482780e0fa 100644 --- a/apps/sandbox/src/app/(sandbox)/templates/page.tsx +++ b/apps/sandbox/src/app/(sandbox)/templates/page.tsx @@ -37,7 +37,8 @@ import { } from '@astryxdesign/core/theme/tokens.stylex'; import {Toolbar} from '@astryxdesign/core/Toolbar'; import {useToast} from '@astryxdesign/core/Toast'; -import {Drawer, Stat} from '@astryxdesign/lab'; +import {Drawer} from '@astryxdesign/core/Drawer'; +import {Stat} from '@astryxdesign/lab'; import { TEMPLATE_AUDIT_CATEGORIES, diff --git a/apps/storybook/rtl-audit/targets.json b/apps/storybook/rtl-audit/targets.json index c3e69a50cb582..b128c5041fb76 100644 --- a/apps/storybook/rtl-audit/targets.json +++ b/apps/storybook/rtl-audit/targets.json @@ -374,7 +374,7 @@ }, { "component": "Drawer", - "storyId": "lab-drawer--sides", + "storyId": "core-drawer--sides", "dims": [ "D4" ], diff --git a/apps/storybook/stories/Drawer.stories.tsx b/apps/storybook/stories/Drawer.stories.tsx index 2ca2c12c657d5..ef0b5dfad1c80 100644 --- a/apps/storybook/stories/Drawer.stories.tsx +++ b/apps/storybook/stories/Drawer.stories.tsx @@ -2,7 +2,7 @@ import type {Meta, StoryObj} from '@storybook/react'; import {useState} from 'react'; -import {Drawer} from '@astryxdesign/lab'; +import {Drawer} from '@astryxdesign/core/Drawer'; import {Button} from '@astryxdesign/core/Button'; import {CheckboxInput} from '@astryxdesign/core/CheckboxInput'; import {Divider} from '@astryxdesign/core/Divider'; @@ -12,7 +12,7 @@ import {VStack, HStack} from '@astryxdesign/core/Stack'; import {Text} from '@astryxdesign/core/Text'; const meta: Meta = { - title: 'Lab/Drawer', + title: 'Core/Drawer', component: Drawer, tags: ['autodocs'], parameters: { diff --git a/docs/architecture/layer-runtime.md b/docs/architecture/layer-runtime.md index 7127c096df071..220fdc752ea8a 100644 --- a/docs/architecture/layer-runtime.md +++ b/docs/architecture/layer-runtime.md @@ -14,7 +14,7 @@ applies_to: packages/core/src/Layer/, packages/core/src/Popover/, packages/core/src/Dialog/, - packages/lab/src/Drawer/, + packages/core/src/Drawer/, packages/core/src/DropdownMenu/, packages/core/src/Tooltip/, packages/core/src/HoverCard/, @@ -153,7 +153,7 @@ contract. The current shared stack registers present layers with `close` or active-cycle registration sequence. `useFocusTrap` adapts an active trap with `onEscape` into that stack. -Tooltip, HoverCard, Dialog, Lab Drawer, Popover, DropdownMenu, Lightbox, and +Tooltip, HoverCard, Dialog, Drawer, Popover, DropdownMenu, Lightbox, and MobileNav all register with the shared stack. Tooltip and HoverCard report current DOM presence; Popover and DropdownMenu register through `useFocusTrap`; Dialog, Lightbox, and MobileNav additionally ask `shouldDismissOnCloseRequest()` before acting on native @@ -297,8 +297,8 @@ be updated only as that work ships. menu-cascade parent-close chain. - Dialog families own native modal/backdrop presentation and their local channel policies. -- Lab Drawer owns modal `showModal()` and non-modal `showPopover()` hosting while - the shared dismissal stack owns Escape and platform close routing. +- Drawer owns modal `showModal()` and non-modal `showPopover()` hosting while the + shared dismissal stack owns Escape and platform close routing. - `LayerProvider`, `ToastContext`, `useToast`, and `ToastViewport` own current notification state, dispatch, and viewport rendering. - CommandPalette owns command search and selection; Dialog owns its native modal diff --git a/docs/families/overlay-dismissal.md b/docs/families/overlay-dismissal.md index 74c7f3c410022..aff91eeba5013 100644 --- a/docs/families/overlay-dismissal.md +++ b/docs/families/overlay-dismissal.md @@ -15,7 +15,7 @@ verified_by: packages/core/src/Layer/useLayerDismissal.test.tsx, packages/core/src/Layer/layerDismissalInvariants.test.tsx, packages/core/src/Layer/layerDismissalFamilies.test.tsx, - packages/lab/src/Drawer/Drawer.test.tsx, + packages/core/src/Drawer/Drawer.test.tsx, packages/core/src/hooks/useFocusTrap.test.tsx, ] members: @@ -90,7 +90,7 @@ this record's current membership snapshot must be updated with it. - **Current members:** Dialog, AlertDialog, Popover, DropdownMenu, DropdownMenuSubMenu, MoreMenu, Tooltip, HoverCard, Lightbox, MobileNav, BottomSheet, BottomSheetSwitcher, CommandPalette, ContextMenu, - PowerSearchEditPopover, Lab Drawer, and the component-owned popup surfaces + PowerSearchEditPopover, Drawer, and the component-owned popup surfaces listed below. - **Component-owned input popups:** ChatComposerInput, ComplexSelector, DateInput, DateRangeInput, DateTimeInput, Selector, MultiSelector, @@ -189,7 +189,7 @@ this record's current membership snapshot must be updated with it. | Components or surface | Adoption | Current deviation or limitation | | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Dialog, AlertDialog, Popover, DropdownMenu root, MoreMenu, Lightbox, MobileNav, Lab Drawer | shared owner | none | +| Dialog, AlertDialog, Popover, DropdownMenu root, MoreMenu, Lightbox, MobileNav, Drawer | shared owner | none | | Tooltip, HoverCard | shared owner with DOM presence reporting | Neither provides nesting depth to descendant layers | | Focus traps with `onEscape` | shared owner through `useFocusTrap` | They provide DOM containment, not descendant depth | | BreadcrumbItem, ChatComposerInput, ComplexSelector, DateInput, DateRangeInput, DateTimeInput, Selector, MultiSelector, PowerSearch, BaseTypeahead, Typeahead, Tokenizer, SideNavHeading, SideNavItem, TabMenu, TopNavHeading, TopNavMenu, TopNavMegaMenu, Table, Lab TourStep, Lab ChatEmojiPicker | shared owner through `usePopover` or a composed Popover owner | Adaptive BottomSheet paths inherit BottomSheet's adoption gap; Table filtering owns controlled Popover state and discards its draft on close; TourStep routes Popover close to the Tour; ChatEmojiPicker owns controlled Popover state | diff --git a/docs/specs/AST-027/spec.md b/docs/specs/AST-027/spec.md index 545ee01f332f5..f26acf8ed2fa3 100644 --- a/docs/specs/AST-027/spec.md +++ b/docs/specs/AST-027/spec.md @@ -186,7 +186,7 @@ production declarations across 40 files and these current gaps: | BottomSheet and BottomSheetSwitcher non-modal paths | Non-modal `dialog.show()` paths use static or dynamic values beginning at `1000` | **Confirmed global leaks.** A page band cannot provide dialog-layer semantics | Route non-modal hosting through Layer/native top-layer infrastructure; keep panel, handle, tint, action, and stack ordering locally isolated inside the host | Current source already routes Tooltip, HoverCard, Popover, DropdownMenu and its -submenus, ContextMenu, CommandPalette, modal Dialog, Lab Drawer, Lightbox, +submenus, ContextMenu, CommandPalette, modal Dialog, Drawer, Lightbox, MobileNav, Tour, Date-input layers, and RadialTooltip through Layer, native Popover, or `showModal()`. Their cross-page mechanism is directionally conforming. The confirmed exceptions are ChartTooltip and the non-modal diff --git a/packages/cli/assets/codemods/transforms/next/__tests__/migrate-lab-drawer-imports.test.mjs b/packages/cli/assets/codemods/transforms/next/__tests__/migrate-lab-drawer-imports.test.mjs new file mode 100644 index 0000000000000..20f5bcd8364a1 --- /dev/null +++ b/packages/cli/assets/codemods/transforms/next/__tests__/migrate-lab-drawer-imports.test.mjs @@ -0,0 +1,79 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +import {describe, expect, it} from 'vitest'; +import jscodeshift from 'jscodeshift'; +import transform from '../migrate-lab-drawer-imports.mjs'; + +const j = jscodeshift.withParser('tsx'); +const api = {jscodeshift: j, stats: () => {}, report: () => {}}; + +function apply(source) { + return transform({source, path: 'test.tsx'}, api) ?? source; +} + +describe('migrate-lab-drawer-imports', () => { + it('moves Drawer and DrawerProps out of a mixed Lab root import', () => { + const output = + apply(`import {Drawer, type DrawerProps, Stat} from '@astryxdesign/lab'; +const drawer: DrawerProps = {label: 'Details'}; +const el = Details;`); + + expect(output).toContain( + "import { Drawer, type DrawerProps } from '@astryxdesign/core/Drawer';", + ); + expect(output).toContain("import { Stat } from '@astryxdesign/lab';"); + }); + + it('preserves a type-only import declaration', () => { + const output = apply("import type {DrawerProps} from '@astryxdesign/lab';"); + + expect(output).toContain( + "import type {DrawerProps} from '@astryxdesign/core/Drawer';", + ); + }); + + it('preserves local aliases', () => { + const output = apply( + "import {Drawer as SidePanel} from '@astryxdesign/lab';", + ); + + expect(output).toContain( + "import {Drawer as SidePanel} from '@astryxdesign/core/Drawer';", + ); + }); + + it('rewrites direct Drawer subpath imports', () => { + const output = apply("import {Drawer} from '@astryxdesign/lab/Drawer';"); + + expect(output).toContain("from '@astryxdesign/core/Drawer'"); + expect(output).not.toContain('@astryxdesign/lab/Drawer'); + }); + + it('splits mixed root re-exports', () => { + const output = apply( + "export {Drawer, type DrawerProps, Stat} from '@astryxdesign/lab';", + ); + + expect(output).toContain( + "export { Drawer, type DrawerProps } from '@astryxdesign/core/Drawer';", + ); + expect(output).toContain("export { Stat } from '@astryxdesign/lab';"); + }); + + it('rewrites direct Drawer export-all declarations', () => { + const output = apply("export * from '@astryxdesign/lab/Drawer';"); + + expect(output).toContain("export * from '@astryxdesign/core/Drawer';"); + }); + + it('leaves unrelated Lab imports unchanged', () => { + const source = "import {Stat} from '@astryxdesign/lab';"; + expect(apply(source)).toBe(source); + }); + + it('is idempotent', () => { + const source = "import {Drawer} from '@astryxdesign/lab';"; + const once = apply(source); + expect(apply(once)).toBe(once); + }); +}); diff --git a/packages/cli/assets/codemods/transforms/next/index.mjs b/packages/cli/assets/codemods/transforms/next/index.mjs index 2af3bdfcf09ad..15c22b4ae213f 100644 --- a/packages/cli/assets/codemods/transforms/next/index.mjs +++ b/packages/cli/assets/codemods/transforms/next/index.mjs @@ -7,4 +7,14 @@ * this file into the resolved version folder. */ -export default []; +import migrateLabDrawerImports, { + meta as migrateLabDrawerImportsMeta, +} from './migrate-lab-drawer-imports.mjs'; + +export default [ + { + name: 'migrate-lab-drawer-imports', + transform: migrateLabDrawerImports, + meta: migrateLabDrawerImportsMeta, + }, +]; diff --git a/packages/cli/assets/codemods/transforms/next/migrate-lab-drawer-imports.mjs b/packages/cli/assets/codemods/transforms/next/migrate-lab-drawer-imports.mjs new file mode 100644 index 0000000000000..db7e086031a35 --- /dev/null +++ b/packages/cli/assets/codemods/transforms/next/migrate-lab-drawer-imports.mjs @@ -0,0 +1,140 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** + * @file Codemod: Repoint Lab Drawer imports and re-exports to Core + */ + +export const meta = { + title: 'Repoint Lab Drawer imports to Core', + description: + 'Moves Drawer and DrawerProps imports from @astryxdesign/lab to @astryxdesign/core/Drawer.', + pr: '#6071', +}; + +const LAB_ROOT = '@astryxdesign/lab'; +const LAB_DRAWER = '@astryxdesign/lab/Drawer'; +const CORE_DRAWER = '@astryxdesign/core/Drawer'; +const DRAWER_EXPORTS = new Set(['Drawer', 'DrawerProps']); + +/** + * @param {any} identifier + * @returns {string} + */ +function identifierName(identifier) { + return identifier?.name ?? identifier?.value ?? ''; +} + +/** @param {any} specifier */ +function isDrawerImportSpecifier(specifier) { + return ( + specifier.type === 'ImportSpecifier' && + DRAWER_EXPORTS.has(identifierName(specifier.imported)) + ); +} + +/** @param {any} specifier */ +function isDrawerExportSpecifier(specifier) { + return ( + specifier.type === 'ExportSpecifier' && + DRAWER_EXPORTS.has(identifierName(specifier.local)) + ); +} + +/** @param {string} source */ +function isLabDrawerSubpath(source) { + return source === LAB_DRAWER || source.startsWith(`${LAB_DRAWER}/`); +} + +/** + * @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) => { + const source = path.node.source.value; + if (typeof source !== 'string') return; + + if (isLabDrawerSubpath(source)) { + path.node.source.value = CORE_DRAWER; + hasChanges = true; + return; + } + if (source !== LAB_ROOT) return; + + const drawerSpecifiers = []; + const remainingSpecifiers = []; + for (const specifier of path.node.specifiers ?? []) { + if (isDrawerImportSpecifier(specifier)) { + drawerSpecifiers.push(specifier); + } else { + remainingSpecifiers.push(specifier); + } + } + if (drawerSpecifiers.length === 0) return; + + if (remainingSpecifiers.length === 0) { + path.node.source.value = CORE_DRAWER; + } else { + path.node.specifiers = remainingSpecifiers; + const coreImport = j.importDeclaration( + drawerSpecifiers, + j.literal(CORE_DRAWER), + ); + coreImport.importKind = path.node.importKind; + j(path).insertBefore(coreImport); + } + hasChanges = true; + }); + + root.find(j.ExportNamedDeclaration).forEach((/** @type {any} */ path) => { + const source = path.node.source?.value; + if (typeof source !== 'string') return; + + if (isLabDrawerSubpath(source)) { + path.node.source.value = CORE_DRAWER; + hasChanges = true; + return; + } + if (source !== LAB_ROOT) return; + + const drawerSpecifiers = []; + const remainingSpecifiers = []; + for (const specifier of path.node.specifiers ?? []) { + if (isDrawerExportSpecifier(specifier)) { + drawerSpecifiers.push(specifier); + } else { + remainingSpecifiers.push(specifier); + } + } + if (drawerSpecifiers.length === 0) return; + + if (remainingSpecifiers.length === 0) { + path.node.source.value = CORE_DRAWER; + } else { + path.node.specifiers = remainingSpecifiers; + const coreExport = j.exportNamedDeclaration( + null, + drawerSpecifiers, + j.literal(CORE_DRAWER), + ); + coreExport.exportKind = path.node.exportKind; + j(path).insertBefore(coreExport); + } + hasChanges = true; + }); + + root.find(j.ExportAllDeclaration).forEach((/** @type {any} */ path) => { + const source = path.node.source?.value; + if (typeof source === 'string' && isLabDrawerSubpath(source)) { + path.node.source.value = CORE_DRAWER; + hasChanges = true; + } + }); + + return hasChanges ? root.toSource({quote: 'single'}) : undefined; +} diff --git a/packages/cli/assets/templates/blocks/components/Drawer/DrawerRowInspector.doc.mjs b/packages/cli/assets/templates/blocks/components/Drawer/DrawerRowInspector.doc.mjs new file mode 100644 index 0000000000000..b7c405fff022f --- /dev/null +++ b/packages/cli/assets/templates/blocks/components/Drawer/DrawerRowInspector.doc.mjs @@ -0,0 +1,22 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */ +export const doc = { + type: 'block', + exampleFor: 'Drawer', + name: 'Drawer — Row inspector', + displayName: 'Drawer — Row inspector', + description: + 'A non-modal master-detail pattern that keeps the underlying row list interactive.', + isReady: true, + aspectRatio: 4 / 3, + componentsUsed: [ + 'Button', + 'Divider', + 'Drawer', + 'Heading', + 'Section', + 'Stack', + 'Text', + ], +}; diff --git a/packages/cli/assets/templates/blocks/components/Drawer/DrawerRowInspector.tsx b/packages/cli/assets/templates/blocks/components/Drawer/DrawerRowInspector.tsx new file mode 100644 index 0000000000000..c102b28e3c1ca --- /dev/null +++ b/packages/cli/assets/templates/blocks/components/Drawer/DrawerRowInspector.tsx @@ -0,0 +1,70 @@ +// Copyright (c) Meta Platforms, Inc. and affiliates. + +'use client'; + +import {useState} from 'react'; +import {Button} from '@astryxdesign/core/Button'; +import {Divider} from '@astryxdesign/core/Divider'; +import {Drawer} from '@astryxdesign/core/Drawer'; +import {Heading} from '@astryxdesign/core/Heading'; +import {Section} from '@astryxdesign/core/Section'; +import {VStack} from '@astryxdesign/core/Stack'; +import {Text} from '@astryxdesign/core/Text'; + +const HOSTS = [ + {id: 'web-01', region: 'us-east-1', status: 'Healthy', cpu: '32%'}, + {id: 'web-02', region: 'us-east-1', status: 'Healthy', cpu: '41%'}, + {id: 'worker-01', region: 'eu-west-1', status: 'Degraded', cpu: '87%'}, +]; + +export default function DrawerRowInspector() { + const [selected, setSelected] = useState(HOSTS[0]); + const [isOpen, setIsOpen] = useState(false); + + return ( + + + {HOSTS.map(host => ( +