Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Generate propTypes #2395

Merged
merged 14 commits into from
Sep 8, 2021
6 changes: 6 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ jobs:
- run:
name: Lint JSON
command: yarn jsonlint
- run:
name: Generate PropTypes
command: yarn proptypes
- run:
name: '`yarn proptypes` changes committed?'
command: git diff --exit-code
- run:
name: Generate the documentation
command: yarn docs:api
Expand Down
7 changes: 6 additions & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ module.exports = {
},
],
],
ignore: [/@babel[\\|/]runtime/], // Fix a Windows issue.
ignore: [
// Fix a Windows issue.
/@babel[\\|/]runtime/,
// Fix const foo = /{{(.+?)}}/gs; crashing.
/prettier/,
Comment on lines +50 to +51
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to work without, is it still needed?

Suggested change
// Fix const foo = /{{(.+?)}}/gs; crashing.
/prettier/,

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it crashes if I remove.

image

],
env: {
coverage: {},
test: {
Expand Down
133 changes: 133 additions & 0 deletions docs/scripts/generateProptypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import * as yargs from 'yargs';
import * as path from 'path';
import * as fse from 'fs-extra';
import * as prettier from 'prettier';
import * as ttp from '@material-ui/monorepo/packages/typescript-to-proptypes/src';
import { fixBabelGeneratorIssues, fixLineEndings } from 'docs/scripts/helpers';

const tsconfig = ttp.loadConfig(path.resolve(__dirname, '../../tsconfig.json'));

const prettierConfig = prettier.resolveConfig.sync(process.cwd(), {
config: path.join(__dirname, '../../prettier.config.js'),
});

async function generateProptypes(program: ttp.ts.Program, sourceFile: string) {
const proptypes = ttp.parseFromProgram(sourceFile, program, { checkDeclarations: true });

if (proptypes.body.length === 0) {
return;
}

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

const result = ttp.inject(proptypes, sourceContent, {
disablePropTypesTypeChecking: true,
comment: [
'----------------------------- Warning --------------------------------',
'| These PropTypes are generated from the TypeScript type definitions |',
'| To update them edit the TypeScript types and run "yarn proptypes" |',
'----------------------------------------------------------------------',
].join('\n'),
reconcilePropTypes: (prop, previous, generated) => {
const usedCustomValidator = previous !== undefined && !previous.startsWith('PropTypes');
const ignoreGenerated =
previous !== undefined &&
previous.startsWith('PropTypes /* @typescript-to-proptypes-ignore */');
return usedCustomValidator || ignoreGenerated ? previous! : generated;
},
shouldInclude: ({ component, prop }) => {
if (['children', 'state'].includes(prop.name) && component.name.startsWith('DataGrid')) {
return false;
}
let shouldDocument = true;
prop.filenames.forEach((filename) => {
// Don't include props from external dependencies
if (/node_modules/.test(filename)) {
shouldDocument = false;
}
});
return shouldDocument;
},
});

if (!result) {
throw new Error('Unable to produce inject propTypes into code.');
}

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

await fse.writeFile(sourceFile, correctedLineEndings);
}

function findComponents(folderPath) {
const files = fse.readdirSync(folderPath, { withFileTypes: true });
return files.reduce((acc, file) => {
if (file.isDirectory()) {
const filesInFolder = findComponents(path.join(folderPath, file.name));
return [...acc, ...filesInFolder];
}
if (/[A-Z]+.*\.tsx/.test(file.name)) {
return [...acc, path.join(folderPath, file.name)];
}
return acc;
}, []);
}

async function run() {
const componentsToAddPropTypes = [
path.resolve(__dirname, '../../packages/grid/data-grid/src/DataGrid.tsx'),
path.resolve(__dirname, '../../packages/grid/x-grid/src/DataGridPro.tsx'),
m4theushw marked this conversation as resolved.
Show resolved Hide resolved
];

const indexPath = path.resolve(__dirname, '../../packages/grid/_modules_/index.ts');
const program = ttp.createTSProgram([...componentsToAddPropTypes, indexPath], tsconfig);
const checker = program.getTypeChecker();
const indexFile = program.getSourceFile(indexPath)!;
const symbol = checker.getSymbolAtLocation(indexFile);
const exports = checker.getExportsOfModule(symbol!);

const componentsFolder = path.resolve(__dirname, '../../packages/grid/_modules_/grid/components');
const components = findComponents(componentsFolder);
components.forEach((component) => {
const componentName = path.basename(component).replace('.tsx', '');
const isExported = exports.find((e) => e.name === componentName);
if (isExported) {
componentsToAddPropTypes.push(component);
}
});

const promises = componentsToAddPropTypes.map<Promise<void>>(async (file) => {
try {
await generateProptypes(program, file);
} catch (error) {
error.message = `${file}: ${error.message}`;
throw error;
}
});

const results = await Promise.allSettled(promises);

const fails = results.filter((result): result is PromiseRejectedResult => {
return result.status === 'rejected';
});

fails.forEach((result) => {
console.error(result.reason);
});
if (fails.length > 0) {
process.exit(1);
}
}

yargs
.command({
command: '$0',
describe: 'Generates Component.propTypes from TypeScript declarations',
handler: run,
})
.help()
.strict(true)
.version(false)
.parse();
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"stylelint": "stylelint '**/*.js' '**/*.ts' '**/*.tsx'",
"prettier": "node ./scripts/prettier.js --branch master",
"prettier:all": "node ./scripts/prettier.js write",
"proptypes": "cross-env BABEL_ENV=development babel-node -i \"/node_modules/(?!@material-ui)/\" -x .ts,.tsx,.js ./docs/scripts/generateProptypes.ts",
"size:snapshot": "node --max-old-space-size=2048 ./scripts/sizeSnapshot/create",
"size:why": "yarn size:snapshot --analyze --accurateBundles",
"test": "lerna run test --parallel",
Expand Down Expand Up @@ -61,6 +62,7 @@
"@material-ui/core": "^4.9.12",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.54",
"@material-ui/utils": "^5.0.0-beta.4",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know why but without adding it here, the build fails. The dependency is already added to the DataGrid's package.json.

"@material-ui/monorepo": "/~https://github.com/mui-org/material-ui.git#next",
"@material-ui/unstyled": "next",
"@rollup/plugin-node-resolve": "^13.0.4",
Expand Down
40 changes: 39 additions & 1 deletion packages/grid/_modules_/grid/components/GridAutoSizer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { useForkRef, ownerWindow } from '@material-ui/core/utils';
import { useEventCallback, useEnhancedEffect } from '../utils/material-ui-utils';
import createDetectElementResize from '../lib/createDetectElementResize';
Expand Down Expand Up @@ -47,7 +48,7 @@ export interface AutoSizerProps extends Omit<React.HTMLAttributes<HTMLDivElement
onResize?: (size: AutoSizerSize) => void;
}

export const GridAutoSizer = React.forwardRef<HTMLDivElement, AutoSizerProps>(function AutoSizer(
const GridAutoSizer = React.forwardRef<HTMLDivElement, AutoSizerProps>(function AutoSizer(
props,
ref,
) {
Expand Down Expand Up @@ -153,3 +154,40 @@ export const GridAutoSizer = React.forwardRef<HTMLDivElement, AutoSizerProps>(fu
</div>
);
});

GridAutoSizer.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "yarn proptypes" |
// ----------------------------------------------------------------------
/**
* Default height to use for initial render; useful for SSR.
* @default null
*/
defaultHeight: PropTypes.number,
/**
* Default width to use for initial render; useful for SSR.
* @default null
*/
defaultWidth: PropTypes.number,
/**
* If `true`, disable dynamic :height property.
* @default false
*/
disableHeight: PropTypes.bool,
/**
* If `true`, disable dynamic :width property.
* @default false
*/
disableWidth: PropTypes.bool,
/**
* Nonce of the inlined stylesheet for Content Security Policy.
*/
nonce: PropTypes.string,
/**
* Callback to be invoked on-resize.
*/
onResize: PropTypes.func,
} as any;

export { GridAutoSizer };
1 change: 1 addition & 0 deletions packages/grid/_modules_/grid/components/GridHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const GridHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<
const PreferencesPanelElement = PreferencesPanelComponent && (
<PreferencesPanelComponent {...apiRef?.current.componentsProps?.preferencesPanel} />
);

const ToolbarComponent = apiRef?.current.components.Toolbar;
const ToolbarElement = ToolbarComponent && (
<ToolbarComponent {...apiRef?.current.componentsProps?.toolbar} />
Expand Down
2 changes: 2 additions & 0 deletions packages/grid/_modules_/grid/components/GridPagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const GridPagination = React.forwardRef<
() => Math.floor(paginationState.rowCount / (paginationState.pageSize || 1)),
[paginationState.rowCount, paginationState.pageSize],
);

const handlePageSizeChange = React.useCallback(
(event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
const newPageSize = Number(event.target.value);
Expand Down Expand Up @@ -95,6 +96,7 @@ export const GridPagination = React.forwardRef<
`Add it to show the pagination select.`,
].join('\n'),
);

warnedOnceMissingPageSizeInRowsPerPageOptions.current = true;
}
}
Expand Down
20 changes: 19 additions & 1 deletion packages/grid/_modules_/grid/components/GridRenderingZone.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { ElementSize } from '../models';
import { gridClasses } from '../gridClasses';

type WithChildren = { children?: React.ReactNode };

export const GridRenderingZone = React.forwardRef<HTMLDivElement, ElementSize & WithChildren>(
const GridRenderingZone = React.forwardRef<HTMLDivElement, ElementSize & WithChildren>(
function GridRenderingZone(props, ref) {
const { height, width, children } = props;
return (
Expand All @@ -21,3 +22,20 @@ export const GridRenderingZone = React.forwardRef<HTMLDivElement, ElementSize &
);
},
);

GridRenderingZone.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "yarn proptypes" |
// ----------------------------------------------------------------------
/**
* The height of a container or HTMLElement.
*/
height: PropTypes.number.isRequired,
/**
* The width of a container or HTMLElement.
*/
width: PropTypes.number.isRequired,
} as any;

export { GridRenderingZone };
16 changes: 15 additions & 1 deletion packages/grid/_modules_/grid/components/GridRow.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { GRID_CSS_CLASS_PREFIX } from '../constants/cssClassesConstants';
import { GridEvents } from '../constants/eventsConstants';
Expand All @@ -17,7 +18,7 @@ export interface GridRowProps {
children: React.ReactNode;
}

export function GridRow(props: GridRowProps) {
function GridRow(props: GridRowProps) {
const { selected, id, rowIndex, children } = props;
const ariaRowIndex = rowIndex + 2; // 1 for the header row and 1 as it's 1 based
const apiRef = useGridApiContext();
Expand Down Expand Up @@ -87,3 +88,16 @@ export function GridRow(props: GridRowProps) {
</div>
);
}

GridRow.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "yarn proptypes" |
// ----------------------------------------------------------------------
children: PropTypes.node,
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
rowIndex: PropTypes.number.isRequired,
selected: PropTypes.bool.isRequired,
} as any;

export { GridRow };
55 changes: 34 additions & 21 deletions packages/grid/_modules_/grid/components/GridRowCount.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { useGridApiContext } from '../hooks/root/useGridApiContext';
import { gridClasses } from '../gridClasses';
Expand All @@ -10,24 +11,36 @@ interface RowCountProps {

type GridRowCountProps = React.HTMLAttributes<HTMLDivElement> & RowCountProps;

export const GridRowCount = React.forwardRef<HTMLDivElement, GridRowCountProps>(
function GridRowCount(props, ref) {
const { className, rowCount, visibleRowCount, ...other } = props;
const apiRef = useGridApiContext();

if (rowCount === 0) {
return null;
}

const text =
visibleRowCount < rowCount
? apiRef.current.getLocaleText('footerTotalVisibleRows')(visibleRowCount, rowCount)
: rowCount.toLocaleString();

return (
<div ref={ref} className={clsx(gridClasses.rowCount, className)} {...other}>
{apiRef.current.getLocaleText('footerTotalRows')} {text}
</div>
);
},
);
const GridRowCount = React.forwardRef<HTMLDivElement, GridRowCountProps>(function GridRowCount(
props,
ref,
) {
const { className, rowCount, visibleRowCount, ...other } = props;
const apiRef = useGridApiContext();

if (rowCount === 0) {
return null;
}

const text =
visibleRowCount < rowCount
? apiRef.current.getLocaleText('footerTotalVisibleRows')(visibleRowCount, rowCount)
: rowCount.toLocaleString();

return (
<div ref={ref} className={clsx(gridClasses.rowCount, className)} {...other}>
{apiRef.current.getLocaleText('footerTotalRows')} {text}
</div>
);
});

GridRowCount.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "yarn proptypes" |
// ----------------------------------------------------------------------
rowCount: PropTypes.number.isRequired,
visibleRowCount: PropTypes.number.isRequired,
} as any;

export { GridRowCount };
Loading