From 155e8cf5f5ae1f4e3fd3b14c4dc45f8890a49826 Mon Sep 17 00:00:00 2001 From: Tom Ross Date: Tue, 12 Jul 2022 15:42:46 +0100 Subject: [PATCH] Support `, + expectedSource: ` + import { mdiClose } from '@mdi/js' + + import { Icon } from '@sourcegraph/wildcard' + + export const Test = + `, + expectedManualChangeMessages: [ + ` + /test.tsx:3:20 - warning: component did not have accessibility attributes and has been hidden from screen readers automatically. Please review manually + >>> + `, + ], + }, + { + label: 'handles direct usage of mdi icons with existing aria attributes', + initialSource: ` + import CloseIcon from 'mdi-react/CloseIcon' + + export const Test = `, + expectedSource: ` + import { mdiClose } from '@mdi/js' + + import { Icon } from '@sourcegraph/wildcard' + + export const Test = + `, + }, + { + label: 'handles direct usage of mdi icons with the size prop', + initialSource: ` + import CloseIcon from 'mdi-react/CloseIcon' + + export const Test = + export const Test2 = `, + expectedSource: ` + import { mdiClose } from '@mdi/js' + + import { Icon } from '@sourcegraph/wildcard' + + export const Test = ( + + ) + export const Test2 = ( + + ) + `, + }, ]) diff --git a/packages/transforms/src/mdiIconToMdiPath/mdiIconToMdiPath.ts b/packages/transforms/src/mdiIconToMdiPath/mdiIconToMdiPath.ts index 6ab4a450..84126a64 100644 --- a/packages/transforms/src/mdiIconToMdiPath/mdiIconToMdiPath.ts +++ b/packages/transforms/src/mdiIconToMdiPath/mdiIconToMdiPath.ts @@ -1,3 +1,6 @@ +import { Node } from 'ts-morph' + +import { addOrUpdateSourcegraphWildcardImportIfNeeded } from '@sourcegraph/codemod-toolkit-packages' import { runTransform, getParentUntilOrThrow, @@ -9,7 +12,7 @@ import { * Convert `` element to `` component. */ export const mdiIconToMdiPath = runTransform(context => { - const { throwManualChangeError } = context + const { throwManualChangeError, addManualChangeLog } = context const mdiIconPathsToImport = new Set() @@ -19,6 +22,76 @@ export const mdiIconToMdiPath = runTransform(context => { } return { + /** + * Handles converting to + */ + JsxSelfClosingElement(jsxElement) { + const tagElementName = jsxElement.getTagNameNode().getText() + const iconRegex = /(\w*.)Icon/ + + if (!tagElementName.match(iconRegex) || !isMdiReactToken(tagElementName)) { + // Not to (we handle correct import later) + jsxElement.set({ + name: 'Icon', + }) + + // Add updated svgPath attribute + jsxElement.addAttribute({ + name: 'svgPath', + initializer: `{${updatedValue}}`, + }) + + // Ensure `inline` is set to false to guarantee that we aren't introducing any new CSS with this change. + jsxElement.addAttribute({ + name: 'inline', + initializer: '{false}', + }) + + // We need to set accessibility attributes on all icons + // If these aren't already set, we default to `aria-hidden={true}` and leave a message to review. + if (!jsxElement.getAttribute('aria-label') && !jsxElement.getAttribute('aria-hidden')) { + jsxElement.addAttribute({ + name: 'aria-hidden', + initializer: '{true}', + }) + + addManualChangeLog({ + node: jsxElement, + message: + ' component did not have accessibility attributes and has been hidden from screen readers automatically. Please review manually', + }) + } + + // Our previous icon library supported a `size` prop, which set height and width. + // We convert this to height and width to be explicit. + const sizeAttribute = jsxElement.getAttribute('size') + if (sizeAttribute && Node.isJsxAttribute(sizeAttribute)) { + jsxElement.addAttribute({ + name: 'height', + initializer: sizeAttribute.getInitializer()?.getText(), + }) + + jsxElement.addAttribute({ + name: 'width', + initializer: sizeAttribute.getInitializer()?.getText(), + }) + + // Remove the old attribute + jsxElement.getAttribute('size')?.remove() + } + + // Store this value so we can import it once finished with this file. + mdiIconPathsToImport.add(updatedValue) + }, + /** + * Handles converting to + */ JsxAttribute(jsxAttribute) { const jsxTagElement = getParentUntilOrThrow(jsxAttribute, isJsxTagElement) if (jsxTagElement.getTagNameNode().getText() !== 'Icon') { @@ -75,9 +148,19 @@ export const mdiIconToMdiPath = runTransform(context => { namedImports: [...mdiIconPathsToImport], moduleSpecifier: '@mdi/js', }) - } - sourceFile.fixUnusedIdentifiers() + // If we're using the component for the first time, + // we need to add the import + addOrUpdateSourcegraphWildcardImportIfNeeded({ + sourceFile, + importStructure: { + namedImports: ['Icon'], + }, + }) + + // Clean up + sourceFile.fixUnusedIdentifiers() + } }, } })