Skip to content

Commit

Permalink
[lab] Migrate Timeline to TypeScript
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviertassinari committed Oct 30, 2020
1 parent e2a8d3a commit 372dd52
Show file tree
Hide file tree
Showing 15 changed files with 108 additions and 75 deletions.
11 changes: 4 additions & 7 deletions docs/pages/api-docs/timeline.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
filename: /packages/material-ui-lab/src/Timeline/Timeline.js
filename: /packages/material-ui-lab/src/Timeline/Timeline.tsx
---

<!--- This documentation is automatically generated, do not try to edit it. -->
Expand All @@ -11,9 +11,9 @@ filename: /packages/material-ui-lab/src/Timeline/Timeline.js
## Import

```js
import Timeline from '@material-ui/lab/Timeline';
import Timeline from '@material-ui/lab/Timeline/Timeline.tsx/Timeline';
// or
import { Timeline } from '@material-ui/lab';
import { Timeline } from '@material-ui/lab/Timeline/Timeline.tsx';
```

You can learn more about the difference by [reading this guide](/guides/minimizing-bundle-size/).
Expand All @@ -28,9 +28,6 @@ The `MuiTimeline` name can be used for providing [default props](/customization/

| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| <span class="prop-name">align</span> | <span class="prop-type">'alternate'<br>&#124;&nbsp;'left'<br>&#124;&nbsp;'right'</span> | <span class="prop-default">'left'</span> | The position where the timeline's content should appear. |
| <span class="prop-name">children</span> | <span class="prop-type">node</span> | | The content of the component. |
| <span class="prop-name">classes</span> | <span class="prop-type">object</span> | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |

The `ref` is forwarded to the root element.

Expand All @@ -51,7 +48,7 @@ You can override the style of the component thanks to one of these customization
- With a [global class name](/customization/components/#overriding-styles-with-global-class-names).
- With a theme and an [`overrides` property](/customization/globals/#css).

If that's not sufficient, you can check the [implementation of the component](https://github.com/mui-org/material-ui/blob/next/packages/material-ui-lab/src/Timeline/Timeline.js) for more detail.
If that's not sufficient, you can check the [implementation of the component](https://github.com/mui-org/material-ui/blob/next/packages/material-ui-lab/src/Timeline/Timeline.tsx) for more detail.

## Demos

Expand Down
6 changes: 3 additions & 3 deletions docs/scripts/buildApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ async function annotateComponentDefinition(context: {
}

const { leadingComments } = node;
const jsdocBlock = leadingComments !== null ? leadingComments[0] : null;
if (leadingComments !== null && leadingComments.length > 1) {
const jsdocBlock = leadingComments != null ? leadingComments[0] : null;
if (leadingComments != null && leadingComments.length > 1) {
throw new Error('Should only have a single leading jsdoc block');
}
if (jsdocBlock != null) {
Expand Down Expand Up @@ -372,7 +372,7 @@ async function buildDocs(options: {
// no Object.assign to visually check for collisions
reactAPI.forwardsRefTo = testInfo.forwardsRefTo;

// if (reactAPI.name !== 'TableCell') {
// if (reactAPI.name !== 'Timeline') {
// return;
// }

Expand Down
2 changes: 1 addition & 1 deletion docs/src/modules/utils/find.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function findPagesMarkdown(
return pagesMarkdown;
}

const componentRegex = /^(Unstable_)?([A-Z][a-z]+)+\.js/;
const componentRegex = /^(Unstable_)?([A-Z][a-z]+)+\.(js|tsx)/;

/**
* Returns the component source in a flat array.
Expand Down
1 change: 1 addition & 0 deletions packages/material-ui-lab/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"build:node": "node ../../scripts/build node",
"build:stable": "node ../../scripts/build stable",
"build:copy-files": "node ../../scripts/copy-files.js",
"build:types": "tsc -p tsconfig.build.json",
"prebuild": "rimraf build",
"release": "yarn build && npm publish build --tag next",
"test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/material-ui-lab/**/*.test.{js,ts,tsx}'",
Expand Down
41 changes: 0 additions & 41 deletions packages/material-ui-lab/src/Timeline/Timeline.d.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Timeline from './Timeline';

describe('<Timeline />', () => {
const mount = createMount();
let classes;
let classes: Record<string, string>;

before(() => {
classes = getClasses(<Timeline />);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { InternalStandardProps as StandardProps } from '@material-ui/core';
import { capitalize } from '@material-ui/core/utils';
import { withStyles } from '@material-ui/core/styles';
import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles';
import TimelineContext from './TimelineContext';

export const styles = () => ({
export const styles = createStyles({
/* Styles applied to the root element. */
root: {
display: 'flex',
Expand All @@ -21,12 +22,41 @@ export const styles = () => ({
alignAlternate: {},
});

const Timeline = React.forwardRef(function Timeline(props, ref) {
export type TimelineClassKey = keyof WithStyles<typeof styles>['classes'];

export interface TimelineProps extends StandardProps<React.HTMLAttributes<HTMLUListElement>> {
/**
* The content of the component.
*/
children?: React.ReactNode;
/**
* Override or extend the styles applied to the component.
*/
classes: {
/** Styles applied to the root element. */
root?: string;
/** Styles applied to the root element if `align="left"`. */
alignLeft?: string;
/** Styles applied to the root element if `align="right"`. */
alignRight?: string;
/** Styles applied to the root element if `align="alternate"`. */
alignAlternate?: string;
};

/**
* The position where the timeline's content should appear.
* @default 'left'
*/
align?: 'left' | 'right' | 'alternate';
}

const Timeline = React.forwardRef<HTMLUListElement, TimelineProps>(function Timeline(props, ref) {
const { align = 'left', classes, className, ...other } = props;

return (
<TimelineContext.Provider value={{ align }}>
<ul
// @ts-expect-error unsafe string concat
className={clsx(classes.root, classes[`align${capitalize(align)}`], className)}
ref={ref}
{...other}
Expand All @@ -35,10 +65,10 @@ const Timeline = React.forwardRef(function Timeline(props, ref) {
);
});

Timeline.propTypes = {
(Timeline as any).propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the d.ts file and run "yarn proptypes" |
// | To update them edit TypeScript types and run "yarn proptypes" |
// ----------------------------------------------------------------------
/**
* The position where the timeline's content should appear.
Expand All @@ -52,11 +82,21 @@ Timeline.propTypes = {
/**
* Override or extend the styles applied to the component.
*/
classes: PropTypes.object,
classes: PropTypes.object.isRequired,
/**
* @ignore
*/
className: PropTypes.string,
};

/**
*
* Demos:
*
* - [Timeline](https://material-ui.com/components/timeline/)
*
* API:
*
* - [Timeline API](https://material-ui.com/api/timeline/)
*/
export default withStyles(styles, { name: 'MuiTimeline' })(Timeline);
1 change: 0 additions & 1 deletion packages/material-ui-lab/src/Timeline/index.js

This file was deleted.

13 changes: 13 additions & 0 deletions packages/material-ui-lab/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"declaration": true,
"rootDir": "./src",
"outDir": "./build",
"emitDeclarationOnly": true
},
"include": ["src/**/*.ts*"],
"exclude": ["src/**/*.d.ts", "src/**/*.test.*", "./**/*.spec.*", "**/test-utils.tsx"],
"references": [{ "path": "../material-ui/tsconfig.build.json" }]
}
2 changes: 1 addition & 1 deletion packages/material-ui-lab/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"extends": "../../tsconfig",
"include": ["src/**/*", "test/**/*", "../material-ui/src/internal/svg-icons/Add.js"]
"include": ["src/**/*", "test/**/*"]
}
2 changes: 1 addition & 1 deletion packages/material-ui/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export type InternalStandardProps<C, Removals extends keyof C = never> = Omit<
> &
// each component declares it's classes in a separate interface for proper JSDOC
StyledComponentProps<never> & {
ref?: C extends { ref?: infer RefType } ? RefType : React.Ref<unknown>;
ref?: C extends { ref?: infer RefType } ? RefType : React.Ref<any>;
// TODO: Remove implicit props. Up to each component.
className?: string;
style?: React.CSSProperties;
Expand Down
17 changes: 16 additions & 1 deletion packages/typescript-to-proptypes/src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ import _ from 'lodash';
import * as t from './types';

export interface GenerateOptions {
/**
* If source itself written in typescript prop-types disable prop-types validation
* by injecting propTypes as
* ```jsx
* .propTypes = { ... } as any
* ```
*/
disableTypescriptPropTypesValidation?: boolean;
/**
* Enable/disable the default sorting (ascending) or provide your own sort function
* @default true
Expand Down Expand Up @@ -86,6 +94,7 @@ function defaultSortLiteralUnions(a: t.LiteralType, b: t.LiteralType) {
*/
export function generate(component: t.Component, options: GenerateOptions = {}): string {
const {
disableTypescriptPropTypesValidation = false,
sortProptypes = true,
importedName = 'PropTypes',
includeJSDoc = true,
Expand Down Expand Up @@ -304,5 +313,11 @@ export function generate(component: t.Component, options: GenerateOptions = {}):
options.comment &&
`// ${options.comment.split(/\r?\n/gm).reduce((prev, curr) => `${prev}\n// ${curr}`)}\n`;

return `${component.name}.propTypes = {\n${comment !== undefined ? comment : ''}${generated}\n}`;
const componentNameNode = disableTypescriptPropTypesValidation
? `(${component.name} as any)`
: component.name;

return `${componentNameNode}.propTypes = {\n${
comment !== undefined ? comment : ''
}${generated}\n}`;
}
1 change: 1 addition & 0 deletions packages/typescript-to-proptypes/src/injector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ export function inject(
plugins: [
require.resolve('@babel/plugin-syntax-class-properties'),
require.resolve('@babel/plugin-syntax-jsx'),
[require.resolve('@babel/plugin-syntax-typescript'), { isTSX: true }],
plugin(propTypes, options, propTypesToInject),
...(babelPlugins || []),
],
Expand Down
32 changes: 20 additions & 12 deletions scripts/generateProptypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ const prettierConfig = prettier.resolveConfig.sync(process.cwd(), {
});

async function generateProptypes(
tsFile: string,
jsFile: string,
program: ttp.ts.Program,
sourceFile: string,
tsFile: string = sourceFile,
): Promise<GenerateResult> {
const proptypes = ttp.parseFromProgram(tsFile, program, {
shouldResolveObject: ({ name }) => {
Expand Down Expand Up @@ -182,20 +182,26 @@ async function generateProptypes(
});
});

const jsContent = await fse.readFile(jsFile, 'utf8');
const sourceContent = await fse.readFile(sourceFile, 'utf8');

const isTsFile = /(\.(ts|tsx))/.test(sourceFile);

const unstyledFile = tsFile.endsWith('Styled.d.ts')
? tsFile.replace(/Styled/g, 'Unstyled')
: null;
const result = ttp.inject(proptypes, jsContent, {

const result = ttp.inject(proptypes, sourceContent, {
removeExistingPropTypes: true,
disableTypescriptPropTypesValidation: isTsFile,
babelOptions: {
filename: jsFile,
filename: sourceFile,
},
comment: [
'----------------------------- Warning --------------------------------',
'| These PropTypes are generated from the TypeScript type definitions |',
'| To update them edit the d.ts file and run "yarn proptypes" |',
isTsFile
? '| To update them edit TypeScript types and run "yarn proptypes" |'
: '| To update them edit the d.ts file and run "yarn proptypes" |',
'----------------------------------------------------------------------',
].join('\n'),
getSortLiteralUnions,
Expand Down Expand Up @@ -254,11 +260,11 @@ async function generateProptypes(
return GenerateResult.Failed;
}

const prettified = prettier.format(result, { ...prettierConfig, filepath: jsFile });
const prettified = prettier.format(result, { ...prettierConfig, filepath: sourceFile });
const formatted = fixBabelGeneratorIssues(prettified);
const correctedLineEndings = fixLineEndings(jsContent, formatted);
const correctedLineEndings = fixLineEndings(sourceContent, formatted);

await fse.writeFile(jsFile, correctedLineEndings);
await fse.writeFile(sourceFile, correctedLineEndings);
return GenerateResult.Success;
}

Expand All @@ -282,7 +288,7 @@ async function run(argv: HandlerArgv) {
path.resolve(__dirname, '../packages/material-ui/src'),
path.resolve(__dirname, '../packages/material-ui-lab/src'),
].map((folderPath) =>
glob('+([A-Z])*/+([A-Z])*.d.ts', {
glob('+([A-Z])*/+([A-Z])*.*@(d.ts|ts|tsx)', {
absolute: true,
cwd: folderPath,
}),
Expand All @@ -294,13 +300,14 @@ async function run(argv: HandlerArgv) {
// Example: Modal/ModalManager.d.ts
.filter((filePath) => {
const folderName = path.basename(path.dirname(filePath));
const fileName = path.basename(filePath, '.d.ts');
const fileName = path.basename(filePath).replace(/(\.d\.ts|\.tsx|\.ts)/g, '');

return fileName === folderName;
})
.filter((filePath) => {
return filePattern.test(filePath);
});

const program = ttp.createTSProgram(files, tsconfig);

const promises = files.map<Promise<GenerateResult>>(async (tsFile) => {
Expand All @@ -310,7 +317,8 @@ async function run(argv: HandlerArgv) {
return GenerateResult.TODO;
}

return generateProptypes(tsFile, jsFile, program);
const sourceFile = tsFile.includes('.d.ts') ? tsFile.replace('.d.ts', '.js') : tsFile;
return generateProptypes(program, sourceFile, tsFile);
});

const results = await Promise.all(promises);
Expand Down

0 comments on commit 372dd52

Please sign in to comment.