diff --git a/.storybook/blocks/ComponentDetails.jsx b/.storybook/blocks/ComponentDetails.jsx
index 6418e4d8ce0..e72ce6bf4c9 100644
--- a/.storybook/blocks/ComponentDetails.jsx
+++ b/.storybook/blocks/ComponentDetails.jsx
@@ -5,13 +5,16 @@ import React, { useEffect, useState } from "react";
import { Body, Code, Heading } from "./Typography.jsx";
import { DDefinition, DList, DTerm } from "./Layouts.jsx";
-import { fetchToken } from "./utilities.js";
+import { fetchToken, getComponentsByStatus } from "./utilities.js";
import AdobeSVG from "../assets/images/adobe_logo.svg?raw";
import GitHubSVG from "../assets/images/github_logo.svg?raw";
import NpmSVG from "../assets/images/npm_logo.svg?raw";
import WCSVG from "../assets/images/wc_logo.svg?raw";
+// Import the pre-generated migrated components data
+import migratedComponentsData from '../data/migrated-components.json';
+
export const DSet = ({ term, children }) => {
return (
<>
@@ -443,4 +446,56 @@ export const TaggedReleases = () => {
);
};
+/**
+ * Displays a list of all components that have been migrated to Spectrum 2.
+ *
+ * Usage of this doc block within MDX template(s):
+ *
+ */
+export const MigratedComponentsList = () => {
+ const [components, setComponents] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+
+ useEffect(() => {
+ // Try to load components server-side first (Node.js environment)
+ try {
+ // Use the pre-generated data from our JSON file
+ if (migratedComponentsData && migratedComponentsData.components) {
+ setComponents(migratedComponentsData.components);
+ setIsLoading(false);
+ return;
+ }
+
+ // Dynamic loading as fallback
+ const migrated = getComponentsByStatus({ statusType: 'migrated' });
+
+ if (migrated && migrated.length > 0) {
+ setComponents(migrated);
+ }
+ } catch (error) {
+ console.warn('Failed to get migrated components:', error);
+ }
+
+ setIsLoading(false);
+ }, []);
+
+ return (
+
+ {!isLoading ? (
+ <>
+
+ {components.map((component, idx) => (
+ -
+ {component.charAt(0).toUpperCase() + component.slice(1)}
+
+ ))}
+
+ >
+ ) : (
+ Loading migrated components...
+ )}
+
+ );
+};
+
export default ComponentDetails;
diff --git a/.storybook/blocks/utilities.js b/.storybook/blocks/utilities.js
index a9b733423d2..c1b305c241f 100644
--- a/.storybook/blocks/utilities.js
+++ b/.storybook/blocks/utilities.js
@@ -1,7 +1,48 @@
import spectrum from "@spectrum-css/tokens/dist/json/tokens.json";
-
import { useTheme } from "@storybook/theming";
+// Import fs and path conditionally to avoid browser issues
+let isNode = false;
+let componentUtils = null;
+
+// Check if we're running in Node.js environment
+try {
+ isNode = typeof process !== 'undefined' &&
+ typeof process.versions !== 'undefined' &&
+ typeof process.versions.node !== 'undefined';
+
+ if (isNode) {
+ // Import the component utility functions from migrated-component-scanner.js
+ try {
+ componentUtils = require('../../tasks/migrated-component-scanner');
+ } catch (err) {
+ console.warn('Failed to import component utilities:', err);
+ }
+ }
+} catch (e) {
+ // We're in a browser environment
+ console.log('Running in browser environment, file system functions will be limited');
+}
+
+/**
+ * Gets all component directories that have a specific status
+ * Only works in Node.js environment
+ * @param {Object} options Options for filtering components
+ * @param {string} options.statusType Status type to filter by (e.g., 'migrated')
+ * @returns {string[]} Array of matching component directory names
+ */
+export function getComponentsByStatus(options = {}) {
+ // Check if we're in a Node.js environment
+ if (!isNode || !componentUtils) {
+ console.warn('getComponentsByStatus can only be used in a Node.js environment');
+ return [];
+ }
+
+ const { statusType } = options;
+
+ return componentUtils.getComponentsByStatus(statusType);
+}
+
/**
* A nestable function to search for a token value in the spectrum token data
* - If the key doesn't exist, it will log a warning
diff --git a/.storybook/data/migrated-components.json b/.storybook/data/migrated-components.json
new file mode 100644
index 00000000000..650643b1827
--- /dev/null
+++ b/.storybook/data/migrated-components.json
@@ -0,0 +1,56 @@
+{
+ "total": 85,
+ "migrated": 49,
+ "components": [
+ "actionbar",
+ "actionbutton",
+ "actiongroup",
+ "alertbanner",
+ "avatar",
+ "breadcrumb",
+ "button",
+ "buttongroup",
+ "checkbox",
+ "closebutton",
+ "coachmark",
+ "colorarea",
+ "colorhandle",
+ "colorloupe",
+ "colorslider",
+ "colorwheel",
+ "dial",
+ "dialog",
+ "divider",
+ "dropzone",
+ "fieldgroup",
+ "fieldlabel",
+ "form",
+ "helptext",
+ "icon",
+ "illustratedmessage",
+ "infieldbutton",
+ "infieldprogresscircle",
+ "inlinealert",
+ "link",
+ "opacitycheckerboard",
+ "pagination",
+ "picker",
+ "popover",
+ "progressbar",
+ "progresscircle",
+ "radio",
+ "rating",
+ "search",
+ "statuslight",
+ "stepper",
+ "swatch",
+ "swatchgroup",
+ "switch",
+ "tag",
+ "textfield",
+ "thumbnail",
+ "toast",
+ "tooltip"
+ ],
+ "generatedAt": "2025-05-15T21:09:40.681Z"
+}
\ No newline at end of file
diff --git a/.storybook/guides/s2_migration.mdx b/.storybook/guides/s2_migration.mdx
index c94a30bf8ad..2813efd4a82 100644
--- a/.storybook/guides/s2_migration.mdx
+++ b/.storybook/guides/s2_migration.mdx
@@ -1,5 +1,6 @@
import { Meta, Title } from "@storybook/blocks";
import { ThemeContainer, Heading } from "../blocks";
+import { MigratedComponentsList } from "../blocks/ComponentDetails.jsx";
import GrayMigrationGuide from "../assets/images/gray_migration-guide.png";
@@ -13,6 +14,12 @@ import GrayMigrationGuide from "../assets/images/gray_migration-guide.png";
- Search within: Use a search field with a separate control to filter the search instead.
- Split button: Use a button group to show any additional actions related to the most critical action. Reference [Spectrum documentation](https://spectrum.corp.adobe.com/page/button-group/#Use-a-button-group-to-show-additional-actions) for more information.
+## Migrated components
+
+This is a list of all components that have been fully migrated to Spectrum 2.
+
+
+
## Grays
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
+ * OF ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ */
+
+/* eslint-disable no-console */
+
+/**
+ * This script scans the components directory and generates a list of components
+ * with a migrated status. The output can be saved to a JSON file or printed to console.
+ *
+ * Usage:
+ * node tasks/migrated-component-scanner.js [--output=path/to/output.json]
+ */
+
+const fs = require("fs");
+const path = require("path");
+
+/**
+ * Gets all component directory names from the components folder
+ * @returns {string[]} Array of component directory names
+ */
+function getAllComponentDirectories() {
+ try {
+ // Get the absolute path to the components directory
+ const componentsDir = path.resolve(process.cwd(), "components");
+
+ // Read all directories in the components folder
+ const directories = fs.readdirSync(componentsDir, { withFileTypes: true })
+ .filter(dirent => dirent.isDirectory())
+ .map(dirent => dirent.name)
+ .sort();
+
+ return directories;
+ } catch (error) {
+ console.error("Error getting component directories:", error);
+ return [];
+ }
+}
+
+/**
+ * Gets all component directories that have a specific status
+ * @param {string} statusType Status type to filter by (e.g., 'migrated')
+ * @returns {string[]} Array of matching component directory names
+ */
+function getComponentsByStatus(statusType) {
+ try {
+ const componentsDir = path.resolve(process.cwd(), "components");
+ const directories = getAllComponentDirectories();
+
+ if (!statusType) return directories;
+
+ // Filter directories that have status type in their stories
+ const matchingComponents = directories.filter(dir => {
+ const storiesDir = path.join(componentsDir, dir, "stories");
+
+ // Check if stories directory exists
+ if (!fs.existsSync(storiesDir)) return false;
+
+ // Get all story files
+ const storyFiles = fs.readdirSync(storiesDir)
+ .filter(file => file.endsWith(".stories.js"));
+
+ // Check each story file for status type
+ return storyFiles.some(file => {
+ const storyContent = fs.readFileSync(path.join(storiesDir, file), "utf8");
+ return storyContent.includes(`type: "${statusType}"`);
+ });
+ });
+
+ return matchingComponents;
+ } catch (error) {
+ console.error(`Error getting components with status ${statusType}:`, error);
+ return [];
+ }
+}
+
+/**
+ * Generates a list of migrated components
+ * @returns {Object} Information about migrated components
+ */
+function generateMigratedComponentsReport() {
+ const allComponents = getAllComponentDirectories();
+ const migratedComponents = getComponentsByStatus("migrated");
+
+ return {
+ total: allComponents.length,
+ migrated: migratedComponents.length,
+ components: migratedComponents,
+ generatedAt: new Date().toISOString()
+ };
+}
+
+// Export the functions for use in other modules
+module.exports = {
+ getAllComponentDirectories,
+ getComponentsByStatus,
+ generateMigratedComponentsReport
+};
+
+// Main execution - only runs when script is executed directly in the terminal
+if (require.main === module) {
+ (async () => {
+ const args = process.argv.slice(2);
+ const outputArg = args.find(arg => arg.startsWith("--output="));
+ const outputPath = outputArg ? outputArg.split("=")[1] : null;
+
+ console.log("Scanning for migrated components...");
+ const report = generateMigratedComponentsReport();
+
+ if (outputPath) {
+ const outputDir = path.dirname(outputPath);
+ if (!fs.existsSync(outputDir)) {
+ fs.mkdirSync(outputDir, { recursive: true });
+ }
+
+ fs.writeFileSync(outputPath, JSON.stringify(report, null, 2));
+ console.log(`Report saved to ${outputPath}`);
+ console.log(`Found ${report.migrated} migrated components out of ${report.total} total components.`);
+ } else {
+ console.log("Migrated Components:");
+ console.log(report.components.join(", "));
+ console.log(`\nTotal: ${report.migrated} out of ${report.total} components are migrated.`);
+ }
+ })();
+}