-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: move from isWarning and isError to status prop (#1973)
- update all (sub-)components to use status instead of two sep. props - refactor stories to use the new prop - ensure snapshots do not change (not updating class names at the moment) - add in migration codemod for changes since last alpha release
- Loading branch information
1 parent
76479c1
commit 56066ae
Showing
12 changed files
with
439 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
import { dedent } from 'ts-dedent'; | ||
|
||
import { updateStatusProp } from './alpha14-to-alpha15'; | ||
import { createTestSourceFile } from '../helpers'; | ||
|
||
describe('alpha14-to-alpha15', () => { | ||
it('updates an appropriate isWarning prop', () => { | ||
const sourceFileText = dedent` | ||
import {FieldNote} from '@chanzuckerberg/eds'; | ||
export default function Component() { | ||
return ( | ||
<FieldNote isWarning /> | ||
) | ||
} | ||
`; | ||
|
||
const sourceFile = createTestSourceFile(sourceFileText); | ||
|
||
updateStatusProp({ | ||
file: sourceFile, | ||
}); | ||
|
||
expect(sourceFile.getText()).toEqual(dedent` | ||
import {FieldNote} from '@chanzuckerberg/eds'; | ||
export default function Component() { | ||
return ( | ||
<FieldNote status="warning" /> | ||
) | ||
} | ||
`); | ||
}); | ||
|
||
it('updates an appropriate isError prop', () => { | ||
const sourceFileText = dedent` | ||
import {FieldNote} from '@chanzuckerberg/eds'; | ||
export default function Component() { | ||
return ( | ||
<FieldNote isError /> | ||
) | ||
} | ||
`; | ||
|
||
const sourceFile = createTestSourceFile(sourceFileText); | ||
|
||
updateStatusProp({ | ||
file: sourceFile, | ||
}); | ||
|
||
expect(sourceFile.getText()).toEqual(dedent` | ||
import {FieldNote} from '@chanzuckerberg/eds'; | ||
export default function Component() { | ||
return ( | ||
<FieldNote status="critical" /> | ||
) | ||
} | ||
`); | ||
}); | ||
|
||
it('does not update an isError prop on non-EDS component', () => { | ||
const sourceFileText = dedent` | ||
import {FieldNote} from 'somewhere'; | ||
export default function Component() { | ||
return ( | ||
<FieldNote isError /> | ||
) | ||
} | ||
`; | ||
|
||
const sourceFile = createTestSourceFile(sourceFileText); | ||
|
||
updateStatusProp({ | ||
file: sourceFile, | ||
}); | ||
|
||
expect(sourceFile.getText()).toEqual(dedent` | ||
import {FieldNote} from 'somewhere'; | ||
export default function Component() { | ||
return ( | ||
<FieldNote isError /> | ||
) | ||
} | ||
`); | ||
}); | ||
|
||
it('updates appropriately when both isError and isWarning prop exists', () => { | ||
const sourceFileText = dedent` | ||
import {FieldNote} from '@chanzuckerberg/eds'; | ||
export default function Component() { | ||
return ( | ||
<FieldNote isWarning isError /> | ||
) | ||
} | ||
`; | ||
|
||
const sourceFile = createTestSourceFile(sourceFileText); | ||
|
||
updateStatusProp({ | ||
file: sourceFile, | ||
}); | ||
|
||
expect(sourceFile.getText()).toEqual(dedent` | ||
import {FieldNote} from '@chanzuckerberg/eds'; | ||
export default function Component() { | ||
return ( | ||
<FieldNote status="warning" status="critical" /> | ||
) | ||
} | ||
`); | ||
}); | ||
|
||
it('converts on all component types', () => { | ||
const sourceFileText = dedent` | ||
import {FieldNote, InputField, Select, TextareaField} from '@chanzuckerberg/eds'; | ||
export default function Component() { | ||
return ( | ||
<div> | ||
<InputField isWarning /> | ||
<FieldNote isWarning></FieldNote> | ||
<Select isError></Select> | ||
<TextareaField isError isWarning></Textarea> | ||
</div> | ||
); | ||
} | ||
`; | ||
|
||
const sourceFile = createTestSourceFile(sourceFileText); | ||
|
||
updateStatusProp({ | ||
file: sourceFile, | ||
}); | ||
|
||
expect(sourceFile.getText()).toEqual(dedent` | ||
import {FieldNote, InputField, Select, TextareaField} from '@chanzuckerberg/eds'; | ||
export default function Component() { | ||
return ( | ||
<div> | ||
<InputField status="warning" /> | ||
<FieldNote status="warning"></FieldNote> | ||
<Select status="critical"></Select> | ||
<TextareaField status="warning" status="critical"></Textarea> | ||
</div> | ||
); | ||
} | ||
`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { | ||
type JsxAttribute, | ||
SyntaxKind, | ||
type Project, | ||
type SourceFile, | ||
} from 'ts-morph'; | ||
import { isDesignSystemImport } from '../helpers'; | ||
|
||
export default function migration(project: Project) { | ||
const files = project.getSourceFiles(); | ||
const sourceFiles = files.filter((file) => !file.isDeclarationFile()); | ||
|
||
sourceFiles.forEach((sourceFile) => { | ||
updateStatusProp({ file: sourceFile }); // https://github.com/chanzuckerberg/edu-design-system/pull/1973 | ||
}); | ||
} | ||
|
||
type TransformOptions = { | ||
file: SourceFile; | ||
}; | ||
|
||
const statusComponents = [ | ||
'FieldNote', | ||
'InputField', | ||
'Select', | ||
'TextareaField', | ||
].map((name) => name.toLowerCase()); | ||
|
||
export function updateStatusProp({ file }: TransformOptions) { | ||
// Filter down to the design-system-only imports | ||
const importDeclarations = file | ||
.getImportDeclarations() | ||
.filter( | ||
(importDeclaration) => | ||
!importDeclaration.isTypeOnly() && | ||
isDesignSystemImport(importDeclaration), | ||
); | ||
|
||
const jsxElements = file.getDescendantsOfKind(SyntaxKind.JsxOpeningElement); | ||
const jsxSelfClosingElements = file.getDescendantsOfKind( | ||
SyntaxKind.JsxSelfClosingElement, | ||
); | ||
|
||
// Get the component usages in the given file (which should only work on EDS imports) | ||
[...jsxElements, ...jsxSelfClosingElements].forEach((element) => { | ||
const tagName = element.getTagNameNode().getText(); | ||
const edsTags: string[] = []; | ||
importDeclarations.forEach((importDeclaration) => { | ||
importDeclaration.getNamedImports().forEach((namedImport) => { | ||
edsTags.push(namedImport.getName()); | ||
}); | ||
}); | ||
|
||
if ( | ||
statusComponents.includes(tagName.toLowerCase()) && | ||
edsTags.includes(tagName) | ||
) { | ||
// detect if isWarning exists (at all or with value true) | ||
if ( | ||
isBooleanTrue( | ||
element.getAttribute('isWarning')?.asKind(SyntaxKind.JsxAttribute), | ||
) | ||
) { | ||
element.getAttribute('isWarning')?.remove(); | ||
element.addAttribute({ | ||
name: 'status', | ||
initializer: `"warning"`, | ||
}); | ||
} | ||
|
||
// detect if isError exists (at all or with value true) | ||
if ( | ||
isBooleanTrue( | ||
element.getAttribute('isError')?.asKind(SyntaxKind.JsxAttribute), | ||
) | ||
) { | ||
element.getAttribute('isError')?.remove(); | ||
element.addAttribute({ | ||
name: 'status', | ||
initializer: `"critical"`, | ||
}); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Determine whether the attribute evaluates to being set to true | ||
* | ||
* @param attribute the attribute retrieved from the element node | ||
* @returns whether the elements attribute evaluates to true in JSX (exists or exists AND is true) | ||
*/ | ||
function isBooleanTrue(attribute: JsxAttribute | undefined): boolean { | ||
return ( | ||
(attribute && typeof attribute?.getInitializer() === 'undefined') || | ||
attribute?.getInitializer()?.getText() === '{true}' | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.