Skip to content

Commit b312efc

Browse files
[lab] Migrate Timeline to TypeScript
1 parent 06ac77b commit b312efc

File tree

15 files changed

+107
-75
lines changed

15 files changed

+107
-75
lines changed

docs/pages/api-docs/timeline.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
filename: /packages/material-ui-lab/src/Timeline/Timeline.js
2+
filename: /packages/material-ui-lab/src/Timeline/Timeline.tsx
33
---
44

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

1313
```js
14-
import Timeline from '@material-ui/lab/Timeline';
14+
import Timeline from '@material-ui/lab/Timeline/Timeline.tsx/Timeline';
1515
// or
16-
import { Timeline } from '@material-ui/lab';
16+
import { Timeline } from '@material-ui/lab/Timeline/Timeline.tsx';
1717
```
1818

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

2929
| Name | Type | Default | Description |
3030
|:-----|:-----|:--------|:------------|
31-
| <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. |
32-
| <span class="prop-name">children</span> | <span class="prop-type">node</span> | | The content of the component. |
33-
| <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. |
3431

3532
The `ref` is forwarded to the root element.
3633

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

54-
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.
51+
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.
5552

5653
## Demos
5754

docs/scripts/buildApi.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ async function annotateComponentDefinition(context: {
134134
}
135135

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

375-
// if (reactAPI.name !== 'TableCell') {
375+
// if (reactAPI.name !== 'Timeline') {
376376
// return;
377377
// }
378378

docs/src/modules/utils/find.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function findPagesMarkdown(
4646
return pagesMarkdown;
4747
}
4848

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

5151
/**
5252
* Returns the component source in a flat array.

packages/material-ui-lab/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"build:node": "node ../../scripts/build node",
3030
"build:stable": "node ../../scripts/build stable",
3131
"build:copy-files": "node ../../scripts/copy-files.js",
32+
"build:types": "tsc -p tsconfig.build.json",
3233
"prebuild": "rimraf build",
3334
"release": "yarn build && npm publish build --tag next",
3435
"test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/material-ui-lab/**/*.test.{js,ts,tsx}'",

packages/material-ui-lab/src/Timeline/Timeline.d.ts

Lines changed: 0 additions & 41 deletions
This file was deleted.

packages/material-ui-lab/src/Timeline/Timeline.test.js renamed to packages/material-ui-lab/src/Timeline/Timeline.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Timeline from './Timeline';
44

55
describe('<Timeline />', () => {
66
const mount = createMount();
7-
let classes;
7+
let classes: Record<string, string>;
88

99
before(() => {
1010
classes = getClasses(<Timeline />);

packages/material-ui-lab/src/Timeline/Timeline.js renamed to packages/material-ui-lab/src/Timeline/Timeline.tsx

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import * as React from 'react';
22
import PropTypes from 'prop-types';
33
import clsx from 'clsx';
4+
import { InternalStandardProps as StandardProps } from '@material-ui/core';
45
import { capitalize } from '@material-ui/core/utils';
5-
import { withStyles } from '@material-ui/core/styles';
6+
import { withStyles, createStyles, WithStyles } from '@material-ui/core/styles';
67
import TimelineContext from './TimelineContext';
78

8-
export const styles = () => ({
9+
export const styles = createStyles({
910
/* Styles applied to the root element. */
1011
root: {
1112
display: 'flex',
@@ -21,12 +22,41 @@ export const styles = () => ({
2122
alignAlternate: {},
2223
});
2324

24-
const Timeline = React.forwardRef(function Timeline(props, ref) {
25+
export type TimelineClassKey = keyof WithStyles<typeof styles>['classes'];
26+
27+
export interface TimelineProps extends StandardProps<React.HTMLAttributes<HTMLUListElement>> {
28+
/**
29+
* The content of the component.
30+
*/
31+
children?: React.ReactNode;
32+
/**
33+
* Override or extend the styles applied to the component.
34+
*/
35+
classes: {
36+
/** Styles applied to the root element. */
37+
root?: string;
38+
/** Styles applied to the root element if `align="left"`. */
39+
alignLeft?: string;
40+
/** Styles applied to the root element if `align="right"`. */
41+
alignRight?: string;
42+
/** Styles applied to the root element if `align="alternate"`. */
43+
alignAlternate?: string;
44+
};
45+
46+
/**
47+
* The position where the timeline's content should appear.
48+
* @default 'left'
49+
*/
50+
align?: 'left' | 'right' | 'alternate';
51+
}
52+
53+
const Timeline = React.forwardRef<HTMLUListElement, TimelineProps>(function Timeline(props, ref) {
2554
const { align = 'left', classes, className, ...other } = props;
2655

2756
return (
2857
<TimelineContext.Provider value={{ align }}>
2958
<ul
59+
// @ts-expect-error unsafe string concat
3060
className={clsx(classes.root, classes[`align${capitalize(align)}`], className)}
3161
ref={ref}
3262
{...other}
@@ -35,10 +65,10 @@ const Timeline = React.forwardRef(function Timeline(props, ref) {
3565
);
3666
});
3767

38-
Timeline.propTypes = {
68+
(Timeline as any).propTypes = {
3969
// ----------------------------- Warning --------------------------------
4070
// | These PropTypes are generated from the TypeScript type definitions |
41-
// | To update them edit the d.ts file and run "yarn proptypes" |
71+
// | To update them edit TypeScript types and run "yarn proptypes" |
4272
// ----------------------------------------------------------------------
4373
/**
4474
* The position where the timeline's content should appear.
@@ -52,11 +82,21 @@ Timeline.propTypes = {
5282
/**
5383
* Override or extend the styles applied to the component.
5484
*/
55-
classes: PropTypes.object,
85+
classes: PropTypes.object.isRequired,
5686
/**
5787
* @ignore
5888
*/
5989
className: PropTypes.string,
6090
};
6191

92+
/**
93+
*
94+
* Demos:
95+
*
96+
* - [Timeline](https://material-ui.com/components/timeline/)
97+
*
98+
* API:
99+
*
100+
* - [Timeline API](https://material-ui.com/api/timeline/)
101+
*/
62102
export default withStyles(styles, { name: 'MuiTimeline' })(Timeline);

packages/material-ui-lab/src/Timeline/index.js

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"noEmit": false,
5+
"declaration": true,
6+
"rootDir": "./src",
7+
"outDir": "./build",
8+
"emitDeclarationOnly": true
9+
},
10+
"include": ["src/**/*.ts*"],
11+
"exclude": ["src/**/*.d.ts", "src/**/*.test.*", "./**/*.spec.*", "**/test-utils.tsx"],
12+
"references": [{ "path": "../material-ui/tsconfig.build.json" }]
13+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"extends": "../../tsconfig",
3-
"include": ["src/**/*", "test/**/*", "../material-ui/src/internal/svg-icons/Add.js"]
3+
"include": ["src/**/*", "test/**/*"]
44
}

packages/material-ui/src/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export type InternalStandardProps<C, Removals extends keyof C = never> = Omit<
4141
> &
4242
// each component declares it's classes in a separate interface for proper JSDOC
4343
StyledComponentProps<never> & {
44-
ref?: C extends { ref?: infer RefType } ? RefType : React.Ref<unknown>;
44+
ref?: C extends { ref?: infer RefType } ? RefType : React.Ref<any>;
4545
// TODO: Remove implicit props. Up to each component.
4646
className?: string;
4747
style?: React.CSSProperties;

packages/typescript-to-proptypes/src/generator.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ import _ from 'lodash';
22
import * as t from './types';
33

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

307-
return `${component.name}.propTypes = {\n${comment !== undefined ? comment : ''}${generated}\n}`;
316+
const componentNameNode = disableTypescriptPropTypesValidation
317+
? `(${component.name} as any)`
318+
: component.name;
319+
320+
return `${componentNameNode}.propTypes = {\n${
321+
comment !== undefined ? comment : ''
322+
}${generated}\n}`;
308323
}

packages/typescript-to-proptypes/src/injector.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ export function inject(
381381
plugins: [
382382
require.resolve('@babel/plugin-syntax-class-properties'),
383383
require.resolve('@babel/plugin-syntax-jsx'),
384+
[require.resolve('@babel/plugin-syntax-typescript'), { isTSX: true }],
384385
plugin(propTypes, options, propTypesToInject),
385386
...(babelPlugins || []),
386387
],

scripts/generateProptypes.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,9 @@ const prettierConfig = prettier.resolveConfig.sync(process.cwd(), {
152152
});
153153

154154
async function generateProptypes(
155-
tsFile: string,
156-
jsFile: string,
157155
program: ttp.ts.Program,
156+
sourceFile: string,
157+
tsFile: string = sourceFile,
158158
): Promise<GenerateResult> {
159159
const proptypes = ttp.parseFromProgram(tsFile, program, {
160160
shouldResolveObject: ({ name }) => {
@@ -182,7 +182,9 @@ async function generateProptypes(
182182
});
183183
});
184184

185-
const jsContent = await fse.readFile(jsFile, 'utf8');
185+
const sourceContent = await fse.readFile(sourceFile, 'utf8');
186+
187+
const isTsFile = /(\.(ts|tsx))/.test(sourceFile);
186188

187189
const unstyledFile = tsFile.endsWith('Styled.d.ts')
188190
? tsFile.replace(/material-ui-lab|material-ui-core|Styled/g, (matched) => {
@@ -191,15 +193,18 @@ async function generateProptypes(
191193
})
192194
: null;
193195

194-
const result = ttp.inject(proptypes, jsContent, {
196+
const result = ttp.inject(proptypes, sourceContent, {
195197
removeExistingPropTypes: true,
198+
disableTypescriptPropTypesValidation: isTsFile,
196199
babelOptions: {
197-
filename: jsFile,
200+
filename: sourceFile,
198201
},
199202
comment: [
200203
'----------------------------- Warning --------------------------------',
201204
'| These PropTypes are generated from the TypeScript type definitions |',
202-
'| To update them edit the d.ts file and run "yarn proptypes" |',
205+
isTsFile
206+
? '| To update them edit TypeScript types and run "yarn proptypes" |'
207+
: '| To update them edit the d.ts file and run "yarn proptypes" |',
203208
'----------------------------------------------------------------------',
204209
].join('\n'),
205210
getSortLiteralUnions,
@@ -258,11 +263,11 @@ async function generateProptypes(
258263
return GenerateResult.Failed;
259264
}
260265

261-
const prettified = prettier.format(result, { ...prettierConfig, filepath: jsFile });
266+
const prettified = prettier.format(result, { ...prettierConfig, filepath: sourceFile });
262267
const formatted = fixBabelGeneratorIssues(prettified);
263-
const correctedLineEndings = fixLineEndings(jsContent, formatted);
268+
const correctedLineEndings = fixLineEndings(sourceContent, formatted);
264269

265-
await fse.writeFile(jsFile, correctedLineEndings);
270+
await fse.writeFile(sourceFile, correctedLineEndings);
266271
return GenerateResult.Success;
267272
}
268273

@@ -287,7 +292,7 @@ async function run(argv: HandlerArgv) {
287292
path.resolve(__dirname, '../packages/material-ui/src'),
288293
path.resolve(__dirname, '../packages/material-ui-lab/src'),
289294
].map((folderPath) =>
290-
glob('+([A-Z])*/+([A-Z])*.d.ts', {
295+
glob('+([A-Z])*/+([A-Z])*.*@(d.ts|ts|tsx)', {
291296
absolute: true,
292297
cwd: folderPath,
293298
}),
@@ -299,13 +304,14 @@ async function run(argv: HandlerArgv) {
299304
// Example: Modal/ModalManager.d.ts
300305
.filter((filePath) => {
301306
const folderName = path.basename(path.dirname(filePath));
302-
const fileName = path.basename(filePath, '.d.ts');
307+
const fileName = path.basename(filePath).replace(/(\.d\.ts|\.tsx|\.ts)/g, '');
303308

304309
return fileName === folderName;
305310
})
306311
.filter((filePath) => {
307312
return filePattern.test(filePath);
308313
});
314+
309315
const program = ttp.createTSProgram(files, tsconfig);
310316

311317
const promises = files.map<Promise<GenerateResult>>(async (tsFile) => {
@@ -315,7 +321,8 @@ async function run(argv: HandlerArgv) {
315321
return GenerateResult.TODO;
316322
}
317323

318-
return generateProptypes(tsFile, jsFile, program);
324+
const sourceFile = tsFile.includes('.d.ts') ? tsFile.replace('.d.ts', '.js') : tsFile;
325+
return generateProptypes(program, sourceFile, tsFile);
319326
});
320327

321328
const results = await Promise.all(promises);

0 commit comments

Comments
 (0)