Skip to content

Commit

Permalink
better solution
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviertassinari committed Jan 2, 2024
1 parent a965cd5 commit 602757c
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 86 deletions.
5 changes: 4 additions & 1 deletion docs/pages/joy-ui/api/menu.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,10 @@
"muiName": "JoyMenu",
"forwardsRefTo": "HTMLUListElement",
"filename": "/packages/mui-joy/src/Menu/Menu.tsx",
"inheritance": { "component": "Popper", "pathname": "/base-ui/api/popper/" },
"inheritance": {
"component": "Popper",
"pathname": "/base-ui/react-popper/components-api/#popper"
},
"demos": "<ul><li><a href=\"/joy-ui/react-menu/\">Menu</a></li></ul>",
"cssComponent": false
}
14 changes: 7 additions & 7 deletions packages/api-docs-builder-core/baseUi/generateApiLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,24 @@ export function generateApiLinks(
}

const { value } = build as PromiseFulfilledResult<ComponentReactApi | HookReactApi>;
const { name, demos } = value;
// find a potential # in the pathname
const hashIdx = demos.length > 0 ? demos[0].demoPathname.indexOf('#') : -1;
const hashIdx = value.demos.length > 0 ? value.demos[0].demoPathname.indexOf('#') : -1;

let pathname = null;

if (demos.length > 0) {
if (value.demos.length > 0) {
// make sure the pathname doesn't contain #
pathname = hashIdx >= 0 ? demos[0].demoPathname.substr(0, hashIdx) : demos[0].demoPathname;
pathname =
hashIdx >= 0 ? value.demos[0].demoPathname.substr(0, hashIdx) : value.demos[0].demoPathname;
}

if (pathname !== null) {
// add the new apiLink, where pathame is in format of /react-component/components-api
apiLinks.push({
pathname: `${pathname}${
name.startsWith('use') ? 'hooks-api' : 'components-api'
}/#${kebabCase(name)}`,
title: name,
value.name.startsWith('use') ? 'hooks-api' : 'components-api'
}/#${kebabCase(value.name)}`,
title: value.name,
});
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { getBaseUiComponentInfo } from './getBaseUiComponentInfo';

describe('getBaseUiComponentInfo', () => {
it('return correct info for base component file', () => {
const info = getBaseUiComponentInfo(
const componentInfo = getBaseUiComponentInfo(
path.join(process.cwd(), `/packages/mui-base/src/Button/Button.tsx`),
);
sinon.assert.match(info, {
sinon.assert.match(componentInfo, {
name: 'Button',
apiPathname: '/base-ui/react-button/components-api/#button',
muiName: 'MuiButton',
Expand All @@ -18,9 +18,9 @@ describe('getBaseUiComponentInfo', () => {
),
});

info.readFile();
componentInfo.readFile();

expect(info.getInheritance()).to.deep.equal(null);
expect(componentInfo.getInheritance()).to.deep.equal(null);

let existed = false;
try {
Expand All @@ -29,7 +29,7 @@ describe('getBaseUiComponentInfo', () => {
// eslint-disable-next-line no-empty
} catch (error) {}
if (existed) {
expect(info.getDemos()).to.deep.equal([
expect(componentInfo.getDemos()).to.deep.equal([
{
demoPageTitle: 'Button',
demoPathname: '/base-ui/react-button/',
Expand Down
47 changes: 20 additions & 27 deletions packages/api-docs-builder-core/baseUi/getBaseUiComponentInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,11 @@ import {
import findPagesMarkdown from '@mui-internal/api-docs-builder/utils/findPagesMarkdown';
import { migratedBaseComponents } from './migratedBaseComponents';

function findBaseDemos(
componentName: string,
pagesMarkdown: ReadonlyArray<{
pathname: string;
components: readonly string[];
markdownContent: string;
}>,
) {
return pagesMarkdown
.filter((page) => page.components.includes(componentName))
.map((page) => ({
demoPageTitle: getTitle(page.markdownContent),
demoPathname: fixPathname(page.pathname),
}));
}

export function getBaseUiComponentInfo(filename: string): ComponentInfo {
const { name } = extractPackageFile(filename);
let srcInfo: null | ReturnType<ComponentInfo['readFile']> = null;
if (!name) {
throw new Error(`Could not find the component name from: ${filename}`);
}

export function getBaseUiDemos(name: string, filename?: string) {
// resolve demos, so that we can getch the API url
const allMarkdowns = findPagesMarkdown()
.filter((markdown) => {
if (migratedBaseComponents.some((component) => filename.includes(component))) {
if (migratedBaseComponents.some((component) => (filename ?? name).includes(component))) {
return markdown.filename.match(/[\\/]data[\\/]base[\\/]/);
}
return true;
Expand All @@ -56,14 +34,29 @@ export function getBaseUiComponentInfo(filename: string): ComponentInfo {
};
});

const demos = findBaseDemos(name, allMarkdowns);
const apiPath = getApiPath(demos, name);
return allMarkdowns
.filter((page) => page.components.includes(name))
.map((page) => ({
demoPageTitle: getTitle(page.markdownContent),
demoPathname: fixPathname(page.pathname),
}));
}

export function getBaseUiComponentInfo(filename: string): ComponentInfo {
const { name } = extractPackageFile(filename);
let srcInfo: null | ReturnType<ComponentInfo['readFile']> = null;
if (!name) {
throw new Error(`Could not find the component name from: ${filename}`);
}

const demos = getBaseUiDemos(name, filename);
const apiPath = getApiPath(demos, name) || '';

return {
filename,
name,
muiName: getMuiName(name),
apiPathname: apiPath ?? `/base-ui/api/${kebabCase(name)}/`,
apiPathname: apiPath,
apiPagesDirectory: path.join(process.cwd(), `docs/pages/base-ui/api`),
isSystemComponent: getSystemComponents().includes(name),
readFile: () => {
Expand Down
3 changes: 1 addition & 2 deletions packages/api-docs-builder-core/baseUi/getBaseUiHookInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function getBaseUiHookInfo(filename: string): HookInfo {
const demos = findBaseHooksDemos(name, allMarkdowns);
const apiPath = getApiPath(demos, name);

const result = {
return {
filename,
name,
apiPathname: apiPath ?? `/base-ui/api/${kebabCase(name)}/`,
Expand All @@ -52,7 +52,6 @@ export function getBaseUiHookInfo(filename: string): HookInfo {
},
getDemos: () => demos,
};
return result;
}

function findBaseHooksDemos(
Expand Down
32 changes: 20 additions & 12 deletions packages/api-docs-builder-core/joyUi/getJoyUiComponentInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import {
ComponentInfo,
extractPackageFile,
fixPathname,
getApiPath,
getMuiName,
getSystemComponents,
parseFile,
} from '@mui-internal/api-docs-builder/buildApiUtils';
import findPagesMarkdown from '@mui-internal/api-docs-builder/utils/findPagesMarkdown';
import { getBaseUiDemos } from '@mui-internal/api-docs-builder-core/baseUi/getBaseUiComponentInfo';

export function getJoyUiComponentInfo(filename: string): ComponentInfo {
const { name } = extractPackageFile(filename);
Expand All @@ -34,27 +36,33 @@ export function getJoyUiComponentInfo(filename: string): ComponentInfo {
return null;
}

const urlComponentName = kebabCase(inheritedComponent.replace(/unstyled/i, ''));
const isBaseUi = inheritedComponent.match(/unstyled/i);

if (isBaseUi) {
const inheritedComponentName = inheritedComponent.replace(/unstyled/i, '');
const demos = getBaseUiDemos(inheritedComponentName);
const apiPath = getApiPath(demos, inheritedComponentName);

if (!apiPath) {
throw new Error(`Could not find API path for component: ${name}`);
}

// TODO: build a map for Base UI component name -> API URL path
// or even better, migrate away from the /components-api/ and /hooks-api/ URLs, flatten.
if (inheritedComponent === 'PopperUnstyled') {
return {
name: 'Popper',
apiPathname: `/base-ui/react-popper/components-api/#${urlComponentName}`,
name: inheritedComponentName,
apiPathname: apiPath,
};
}

const urlComponentName = kebabCase(inheritedComponent.replace(/unstyled/i, ''));

// `inheritedComponent` node is coming from test files.
// `inheritedComponent` must include `Unstyled` suffix for parser to recognise that the component inherits Base UI component
// e.g., Joy Menu inherits Base UI Popper, and its test file uses the name `PopperUnstyled` so that we can recognise here that
// Joy Menu is inheriting a base component. In terms of documentation, we should no longer use the name `PopperUnstyled`, and hence
// e.g., Joy UI Menu inherits Base UI Popper, and its test file uses the name `PopperUnstyled` so that we can recognise here that
// Joy UI Menu is inheriting a base component. In terms of documentation, we should no longer use the name `PopperUnstyled`, and hence
// we remove the suffix here.
return {
name: inheritedComponent.replace(/unstyled/i, ''),
apiPathname: `/${
inheritedComponent.match(/unstyled/i) ? 'base-ui' : 'joy-ui'
}/api/${urlComponentName}/`,
name: inheritedComponent,
apiPathname: `/joy-ui/api/${urlComponentName}/`,
};
},
getDemos: () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { getMaterialUiComponentInfo } from './getMaterialUiComponentInfo';

describe('getMaterialUiComponentInfo', () => {
it('return correct info for material component file', () => {
const info = getMaterialUiComponentInfo(
const componentInfo = getMaterialUiComponentInfo(
path.join(process.cwd(), `/packages/mui-material/src/Button/Button.js`),
);
sinon.assert.match(info, {
sinon.assert.match(componentInfo, {
name: 'Button',
apiPathname: '/material-ui/api/button/',
muiName: 'MuiButton',
Expand All @@ -18,7 +18,7 @@ describe('getMaterialUiComponentInfo', () => {
),
});

expect(info.getInheritance('ButtonBase')).to.deep.equal({
expect(componentInfo.getInheritance('ButtonBase')).to.deep.equal({
name: 'ButtonBase',
apiPathname: '/material-ui/api/button-base/',
});
Expand All @@ -30,7 +30,7 @@ describe('getMaterialUiComponentInfo', () => {
// eslint-disable-next-line no-empty
} catch (error) {}
if (existed) {
expect(info.getDemos()).to.deep.equal([
expect(componentInfo.getDemos()).to.deep.equal([
{
demoPageTitle: 'Button Group',
demoPathname: '/material-ui/react-button-group/',
Expand Down
41 changes: 17 additions & 24 deletions packages/api-docs-builder/ApiBuilders/ComponentApiBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -688,28 +688,16 @@ export default async function generateComponentApi(
project: TypeScriptProject,
projectSettings: ProjectSettings,
) {
const {
filename,
name,
muiName,
apiPathname,
apiPagesDirectory,
getInheritance,
getDemos,
readFile,
skipApiGeneration,
isSystemComponent,
} = componentInfo;

const { shouldSkip, spread, EOL, src } = readFile();
const { shouldSkip, spread, EOL, src } = componentInfo.readFile();

if (shouldSkip) {
return null;
}

const filename = componentInfo.filename;
let reactApi: ReactApi;

if (isSystemComponent) {
if (componentInfo.isSystemComponent) {
try {
reactApi = docgenParse(
src,
Expand All @@ -732,7 +720,7 @@ export default async function generateComponentApi(
if (expression.value?.callee) {
const definitionName = expression.value.callee.name;

if (definitionName === `create${name}`) {
if (definitionName === `create${componentInfo.name}`) {
node = expression;
}
}
Expand Down Expand Up @@ -769,12 +757,12 @@ export default async function generateComponentApi(
reactApi.description = reactApi.description.slice(0, annotatedDescriptionMatch.index).trim();
}
reactApi.filename = filename;
reactApi.name = name;
reactApi.imports = getComponentImports(name, filename);
reactApi.muiName = muiName;
reactApi.apiPathname = apiPathname;
reactApi.name = componentInfo.name;
reactApi.imports = getComponentImports(componentInfo.name, filename);
reactApi.muiName = componentInfo.muiName;
reactApi.apiPathname = componentInfo.apiPathname;
reactApi.EOL = EOL;
reactApi.demos = getDemos();
reactApi.demos = componentInfo.getDemos();
if (reactApi.demos.length === 0) {
throw new Error(
'Unable to find demos. \n' +
Expand All @@ -788,7 +776,7 @@ export default async function generateComponentApi(
reactApi.forwardsRefTo = testInfo.forwardsRefTo;
reactApi.spread = testInfo.spread ?? spread;
reactApi.themeDefaultProps = testInfo.themeDefaultProps;
reactApi.inheritance = getInheritance(testInfo.inheritComponent);
reactApi.inheritance = componentInfo.getInheritance(testInfo.inheritComponent);

const { slots, classes } = parseSlotsAndClasses({
project,
Expand All @@ -808,7 +796,7 @@ export default async function generateComponentApi(
const normalizedApiPathname = reactApi.apiPathname.replace(/\\/g, '/');
const normalizedFilename = reactApi.filename.replace(/\\/g, '/');

if (!skipApiGeneration) {
if (!componentInfo.skipApiGeneration) {
// Generate pages, json and translations
let translationPagesDirectory = 'docs/translations/api-docs';
if (normalizedApiPathname.startsWith('/joy-ui') && normalizedFilename.includes('mui-joy/src')) {
Expand All @@ -828,7 +816,12 @@ export default async function generateComponentApi(

// Once we have the tabs API in all projects, we can make this default
const generateOnlyJsonFile = normalizedApiPathname.startsWith('/base');
generateApiPage(apiPagesDirectory, translationPagesDirectory, reactApi, generateOnlyJsonFile);
generateApiPage(
componentInfo.apiPagesDirectory,
translationPagesDirectory,
reactApi,
generateOnlyJsonFile,
);

// Add comment about demo & api links (including inherited component) to the component file
await annotateComponentDefinition(reactApi);
Expand Down
6 changes: 3 additions & 3 deletions packages/api-docs-builder/buildApiUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,10 @@ export type HookInfo = {
skipApiGeneration?: boolean;
};

export const getApiPath = (
export function getApiPath(
demos: Array<{ demoPageTitle: string; demoPathname: string }>,
name: string,
) => {
) {
let apiPath = null;

if (demos && demos.length > 0) {
Expand All @@ -175,7 +175,7 @@ export const getApiPath = (
}

return apiPath;
};
}

export function formatType(rawType: string) {
if (!rawType) {
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-joy/src/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ const MenuRoot = styled(StyledList, {
* API:
*
* - [Menu API](https://mui.com/joy-ui/api/menu/)
* - inherits [Popper API](https://mui.com/base-ui/api/popper/)
* - inherits [Popper API](https://mui.com/base-ui/react-popper/components-api/#popper)
*/
const Menu = React.forwardRef(function Menu(inProps, ref: React.ForwardedRef<HTMLUListElement>) {
const props = useThemeProps({
Expand Down

0 comments on commit 602757c

Please sign in to comment.