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 ? ( + <> + + + ) : ( + 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.`); + } + })(); +}