Skip to content

Commit

Permalink
[docs] Add ref forwarding to API docs (#15135)
Browse files Browse the repository at this point in the history
Adds explicit documentation to `https://material-ui.com/api/*` about ref forwarding. Requires #15131 to not report false positives.

Finishes one point in #14415.
  • Loading branch information
eps1lon committed Apr 4, 2019
1 parent 8db4931 commit f1e2e55
Show file tree
Hide file tree
Showing 117 changed files with 355 additions and 14 deletions.
13 changes: 11 additions & 2 deletions docs/scripts/buildApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { parse as docgenParse } from 'react-docgen';
import generateMarkdown from '../src/modules/utils/generateMarkdown';
import { findPagesMarkdown, findComponents } from '../src/modules/utils/find';
import { getHeaders } from '../src/modules/utils/parseMarkdown';
import parseTest from '../src/modules/utils/parseTest';
import createMuiTheme from '../../packages/material-ui/src/styles/createMuiTheme';
import getStylesCreator from '../../packages/material-ui-styles/src/getStylesCreator';

Expand Down Expand Up @@ -73,7 +74,7 @@ function getInheritance(src) {
};
}

function buildDocs(options) {
async function buildDocs(options) {
const { component: componentObject, pagesMarkdown } = options;
const src = readFileSync(componentObject.filename, 'utf8');

Expand Down Expand Up @@ -143,6 +144,10 @@ function buildDocs(options) {
reactAPI.src = src;
reactAPI.spread = spread;

const testInfo = await parseTest(componentObject.filename);
// no Object.assign to visually check for collisions
reactAPI.forwardsRefTo = testInfo.forwardsRefTo;

// if (reactAPI.name !== 'TableCell') {
// return;
// }
Expand Down Expand Up @@ -199,7 +204,11 @@ function run() {
const components = findComponents(path.resolve(rootDirectory, args[2]));

components.forEach(component => {
buildDocs({ component, pagesMarkdown });
buildDocs({ component, pagesMarkdown }).catch(error => {
console.warn(`error building docs for ${component.filename}`);
console.error(error);
process.exit(1);
});
});
}

Expand Down
8 changes: 8 additions & 0 deletions docs/src/modules/utils/generateMarkdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,14 @@ function generateProps(reactAPI) {
return textProps;
}, text);

let refHint = 'The `ref` is forwarded to the root element.';
if (reactAPI.forwardsRefTo == null) {
refHint = 'The component cannot hold a ref.';
} else if (reactAPI.forwardsRefTo === 'React.Component') {
refHint = 'The `ref` is attached to a component class.';
}
text = `${text}\n${refHint}\n`;

if (reactAPI.spread) {
text = `${text}
Any other properties supplied will be spread to the root element (${
Expand Down
102 changes: 102 additions & 0 deletions docs/src/modules/utils/parseTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import * as babel from '@babel/core';
import { readFile } from 'fs-extra';
import * as path from 'path';

const workspaceRoot = path.join(__dirname, '../../../../');
const babelConfigPath = path.join(workspaceRoot, 'babel.config.js');

function withExtension(filepath, extension) {
return path.join(
path.dirname(filepath),
path.basename(filepath, path.extname(filepath)) + extension,
);
}

/**
* @param {string} filename
* @param {string} configFilePath
*/
async function parseWithConfig(filename, configFilePath) {
const source = await readFile(filename, { encoding: 'utf8' });
const partialConfig = babel.loadPartialConfig({
configFile: configFilePath,
filename,
});
return babel.parseAsync(source, partialConfig.options);
}

function findConformanceDescriptor(program) {
const { types: t } = babel;

let descriptor = {};
babel.traverse(program, {
CallExpression(babelPath) {
const { node: callExpression } = babelPath;
const { callee } = callExpression;
if (t.isIdentifier(callee) && callee.name === 'describeConformance') {
// describeConformance(element, () => options);
descriptor = callExpression.arguments[1].body;
}
},
});

if (descriptor.type != null && !t.isObjectExpression(descriptor)) {
throw new Error(`Expected an object expression as a descriptor but found ${descriptor.type}`);
}

return descriptor;
}

/**
*
* @param {import('@babel/core').Node} valueNode
*/
function getRefInstance(valueNode) {
if (!babel.types.isMemberExpression(valueNode)) {
throw new Error('Expected a member expression in refInstanceof');
}

switch (valueNode.object.name) {
case 'window':
return valueNode.property.name;
case 'React':
return `React.${valueNode.property.name}`;
default:
throw new Error(`Unrecognized member expression starting with '${valueNode.object.name}'`);
}
}

/**
* @typedef {Object} ParseResult
* @property {string?} forwardsRefTo
*/

/**
*
* @param {string} componentFilename
* @returns {ParseResult}
*/
export default async function parseTest(componentFilename) {
const testFilename = withExtension(componentFilename, '.test.js');
const babelParseResult = await parseWithConfig(testFilename, babelConfigPath);
const descriptor = findConformanceDescriptor(babelParseResult.program);

const result = {
forwardsRefTo: undefined,
};

const { properties = [] } = descriptor;
properties.forEach(property => {
const key = property.key.name;

switch (key) {
case 'refInstanceof':
result.forwardsRefTo = getRefInstance(property.value);
break;
default:
break;
}
});

return result;
}
20 changes: 8 additions & 12 deletions packages/material-ui/src/ButtonBase/TouchRipple.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { assert } from 'chai';
import {
createShallow,
createMount,
findOutermostIntrinsic,
describeConformance,
getClasses,
unwrap,
} from '@material-ui/core/test-utils';
Expand All @@ -29,17 +29,13 @@ describe('<TouchRipple />', () => {
mount.cleanUp();
});

it('should render a span', () => {
const wrapper = mount(<TouchRipple />);
const root = findOutermostIntrinsic(wrapper);
assert.strictEqual(root.type(), 'span');
assert.strictEqual(root.hasClass(classes.root), true);
});

it('should render the custom className', () => {
const wrapper = mount(<TouchRipple className="test-class-name" />);
assert.strictEqual(findOutermostIntrinsic(wrapper).hasClass('test-class-name'), true);
});
describeConformance(<TouchRipple />, () => ({
classes,
inheritComponent: 'span',
mount,
refInstanceof: React.Component,
testComponentPropWith: false,
}));

describe('prop: center', () => {
it('should should compute the right ripple dimensions', () => {
Expand Down
2 changes: 2 additions & 0 deletions pages/api/app-bar.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import AppBar from '@material-ui/core/AppBar';
| <span class="prop-name">color</span> | <span class="prop-type">enum:&nbsp;'inherit'&nbsp;&#124;<br>&nbsp;'primary'&nbsp;&#124;<br>&nbsp;'secondary'&nbsp;&#124;<br>&nbsp;'default'<br></span> | <span class="prop-default">'primary'</span> | The color of the component. It supports those theme colors that make sense for this component. |
| <span class="prop-name">position</span> | <span class="prop-type">enum:&nbsp;'fixed', 'absolute', 'sticky', 'static', 'relative'<br></span> | <span class="prop-default">'fixed'</span> | The positioning type. The behavior of the different options is described [in the MDN web docs](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Positioning). Note: `sticky` is not universally supported and will fall back to `static` when unavailable. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element ([Paper](/api/paper/)).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/avatar.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import Avatar from '@material-ui/core/Avatar';
| <span class="prop-name">src</span> | <span class="prop-type">string</span> | | The `src` attribute for the `img` element. |
| <span class="prop-name">srcSet</span> | <span class="prop-type">string</span> | | The `srcSet` attribute for the `img` element. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/backdrop.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import Backdrop from '@material-ui/core/Backdrop';
| <span class="prop-name required">open&nbsp;*</span> | <span class="prop-type">bool</span> | | If `true`, the backdrop is open. |
| <span class="prop-name">transitionDuration</span> | <span class="prop-type">union:&nbsp;number&nbsp;&#124;<br>&nbsp;{ enter?: number, exit?: number }<br></span> | | The duration for the transition, in milliseconds. You may specify a single timeout for all transitions, or individually with an object. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/badge.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import Badge from '@material-ui/core/Badge';
| <span class="prop-name">showZero</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | Controls whether the badge is hidden when `badgeContent` is zero. |
| <span class="prop-name">variant</span> | <span class="prop-type">enum:&nbsp;'standard'&nbsp;&#124;<br>&nbsp;'dot'<br></span> | <span class="prop-default">'standard'</span> | The variant to use. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/bottom-navigation-action.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';
| <span class="prop-name">showLabel</span> | <span class="prop-type">bool</span> | | If `true`, the `BottomNavigationAction` will show its label. By default, only the selected `BottomNavigationAction` inside `BottomNavigation` will show its label. |
| <span class="prop-name">value</span> | <span class="prop-type">any</span> | | You can provide your own value. Otherwise, we fallback to the child position index. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element ([ButtonBase](/api/button-base/)).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/bottom-navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import BottomNavigation from '@material-ui/core/BottomNavigation';
| <span class="prop-name">showLabels</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, all `BottomNavigationAction`s will show their labels. By default, only the selected `BottomNavigationAction` will show its label. |
| <span class="prop-name">value</span> | <span class="prop-type">any</span> | | The value of the currently selected `BottomNavigationAction`. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/breadcrumbs.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import Breadcrumbs from '@material-ui/core/Breadcrumbs';
| <span class="prop-name">maxItems</span> | <span class="prop-type">number</span> | <span class="prop-default">8</span> | Specifies the maximum number of breadcrumbs to display. When there are more than the maximum number, only the first and last will be shown, with an ellipsis in between. |
| <span class="prop-name">separator</span> | <span class="prop-type">node</span> | <span class="prop-default">'/'</span> | Custom separator node. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## Demos
Expand Down
2 changes: 2 additions & 0 deletions pages/api/button-base.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ It contains a load of style reset and some focus/ripple logic.
| <span class="prop-name">TouchRippleProps</span> | <span class="prop-type">object</span> | | Properties applied to the `TouchRipple` element. |
| <span class="prop-name">type</span> | <span class="prop-type">enum:&nbsp;'submit'&nbsp;&#124;<br>&nbsp;'reset'&nbsp;&#124;<br>&nbsp;'button'<br></span> | <span class="prop-default">'button'</span> | Used to control the button's purpose. This property passes the value to the `type` attribute of the native button component. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/button.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import Button from '@material-ui/core/Button';
| <span class="prop-name">size</span> | <span class="prop-type">enum:&nbsp;'small'&nbsp;&#124;<br>&nbsp;'medium'&nbsp;&#124;<br>&nbsp;'large'<br></span> | <span class="prop-default">'medium'</span> | The size of the button. `small` is equivalent to the dense button styling. |
| <span class="prop-name">variant</span> | <span class="prop-type">enum:&nbsp;'text'&nbsp;&#124;<br>&nbsp;'outlined'&nbsp;&#124;<br>&nbsp;'contained'<br></span> | <span class="prop-default">'text'</span> | The variant to use. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element ([ButtonBase](/api/button-base/)).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/card-action-area.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import CardActionArea from '@material-ui/core/CardActionArea';
| <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.

Any other properties supplied will be spread to the root element ([ButtonBase](/api/button-base/)).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/card-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import CardActions from '@material-ui/core/CardActions';
| <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. |
| <span class="prop-name">disableActionSpacing</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the card actions do not have additional margin. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/card-content.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import CardContent from '@material-ui/core/CardContent';
| <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. |
| <span class="prop-name">component</span> | <span class="prop-type">elementType</span> | <span class="prop-default">'div'</span> | The component used for the root node. Either a string to use a DOM element or a component. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/card-header.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import CardHeader from '@material-ui/core/CardHeader';
| <span class="prop-name">title</span> | <span class="prop-type">node</span> | | The content of the Card Title. |
| <span class="prop-name">titleTypographyProps</span> | <span class="prop-type">object</span> | | These props will be forwarded to the title (as long as disableTypography is not `true`). |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/card-media.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import CardMedia from '@material-ui/core/CardMedia';
| <span class="prop-name">image</span> | <span class="prop-type">string</span> | | Image to be displayed as a background image. Either `image` or `src` prop must be specified. Note that caller must specify height otherwise the image will not be visible. |
| <span class="prop-name">src</span> | <span class="prop-type">string</span> | | An alias for `image` property. Available only with media components. Media components: `video`, `audio`, `picture`, `iframe`, `img`. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/card.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import Card from '@material-ui/core/Card';
| <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. |
| <span class="prop-name">raised</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the card will use raised styling. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element ([Paper](/api/paper/)).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/checkbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import Checkbox from '@material-ui/core/Checkbox';
| <span class="prop-name">type</span> | <span class="prop-type">string</span> | | The input component property `type`. |
| <span class="prop-name">value</span> | <span class="prop-type">string</span> | | The value of the component. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element ([IconButton](/api/icon-button/)).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/chip.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Chips represent complex entities in small blocks, such as a contact.
| <span class="prop-name">onDelete</span> | <span class="prop-type">func</span> | | Callback function fired when the delete icon is clicked. If set, the delete icon will be shown. |
| <span class="prop-name">variant</span> | <span class="prop-type">enum:&nbsp;'default'&nbsp;&#124;<br>&nbsp;'outlined'<br></span> | <span class="prop-default">'default'</span> | The variant to use. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/circular-progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ attribute to `true` on that region until it has finished loading.
| <span class="prop-name">value</span> | <span class="prop-type">number</span> | <span class="prop-default">0</span> | The value of the progress indicator for the determinate and static variants. Value between 0 and 100. |
| <span class="prop-name">variant</span> | <span class="prop-type">enum:&nbsp;'determinate'&nbsp;&#124;<br>&nbsp;'indeterminate'&nbsp;&#124;<br>&nbsp;'static'<br></span> | <span class="prop-default">'indeterminate'</span> | The variant to use. Use indeterminate when there is no progress value. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/click-away-listener.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ For instance, if you need to hide a menu when people click anywhere else on your
| <span class="prop-name required">onClickAway&nbsp;*</span> | <span class="prop-type">func</span> | | Callback fired when a "click away" event is detected. |
| <span class="prop-name">touchEvent</span> | <span class="prop-type">enum:&nbsp;'onTouchStart'&nbsp;&#124;<br>&nbsp;'onTouchEnd'&nbsp;&#124;<br>&nbsp;false<br></span> | <span class="prop-default">'onTouchEnd'</span> | The touch event to listen to. You can disable the listener by providing `false`. |

The component cannot hold a ref.

Any other properties supplied will be spread to the root element ([EventListener](https://github.com/oliviertassinari/react-event-listener/)).

## Inheritance
Expand Down
2 changes: 2 additions & 0 deletions pages/api/collapse.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ It uses [react-transition-group](https://github.com/reactjs/react-transition-gro
| <span class="prop-name">in</span> | <span class="prop-type">bool</span> | | If `true`, the component will transition in. |
| <span class="prop-name">timeout</span> | <span class="prop-type">union:&nbsp;number&nbsp;&#124;<br>&nbsp;{ enter?: number, exit?: number }&nbsp;&#124;<br>&nbsp;enum:&nbsp;'auto'<br><br></span> | <span class="prop-default">duration.standard</span> | The duration for the transition, in milliseconds. You may specify a single timeout for all transitions, or individually with an object.<br>Set to 'auto' to automatically calculate transition time based on height. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element ([Transition](https://reactcommunity.org/react-transition-group/#Transition)).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/container.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import Container from '@material-ui/core/Container';
| <span class="prop-name">fixed</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | Set the max-width to match the min-width of the current breakpoint. This is useful if you'd prefer to design for a fixed set of sizes instead of trying to accommodate a fully fluid viewport. It's fluid by default. |
| <span class="prop-name">maxWidth</span> | <span class="prop-type">enum:&nbsp;'xs', 'sm', 'md', 'lg', 'xl', false<br></span> | <span class="prop-default">'lg'</span> | Determine the max-width of the container. The container width grows with the size of the screen. Set to `false` to disable `maxWidth`. |

The `ref` is forwarded to the root element.

Any other properties supplied will be spread to the root element (native element).

## CSS
Expand Down
2 changes: 2 additions & 0 deletions pages/api/css-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Kickstart an elegant, consistent, and simple baseline to build upon.
|:-----|:-----|:--------|:------------|
| <span class="prop-name">children</span> | <span class="prop-type">node</span> | <span class="prop-default">null</span> | You can wrap a node. |

The component cannot hold a ref.


## Demos

Expand Down

0 comments on commit f1e2e55

Please sign in to comment.