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

[base] Make CSS class prefixes consistent #33411

Merged
merged 18 commits into from Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/scripts/ApiBuilders/ComponentApiBuilder.ts
Expand Up @@ -10,6 +10,7 @@ import remark from 'remark';
import remarkVisit from 'unist-util-visit';
import { Link } from 'mdast';
import { defaultHandlers, parse as docgenParse, ReactDocgenApi } from 'react-docgen';
import { unstable_generateUtilityClass as generateUtilityClass } from '@mui/utils';
import muiDefaultPropsHandler from 'docs/src/modules/utils/defaultPropsHandler';
import { LANGUAGES } from 'docs/src/modules/constants';
import parseTest from 'docs/src/modules/utils/parseTest';
Expand All @@ -22,7 +23,6 @@ import createDescribeableProp, {
} from 'docs/src/modules/utils/createDescribeableProp';
import generatePropDescription from 'docs/src/modules/utils/generatePropDescription';
import parseStyles, { Styles } from 'docs/src/modules/utils/parseStyles';
import generateUtilityClass from '@mui/base/generateUtilityClass';
import * as ttp from 'typescript-to-proptypes';
import { getUnstyledFilename } from '../helpers';
import { ComponentInfo } from '../buildApiUtils';
Expand Down
4 changes: 2 additions & 2 deletions packages/mui-base/src/BadgeUnstyled/badgeUnstyledClasses.ts
Expand Up @@ -13,10 +13,10 @@ export interface BadgeUnstyledClasses {
export type BadgeUnstyledClassKey = keyof BadgeUnstyledClasses;

export function getBadgeUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('BaseBadge', slot);
return generateUtilityClass('MuiBadge', slot);
}

const badgeUnstyledClasses: BadgeUnstyledClasses = generateUtilityClasses('BaseBadge', [
const badgeUnstyledClasses: BadgeUnstyledClasses = generateUtilityClasses('MuiBadge', [
'root',
'badge',
'invisible',
Expand Down
4 changes: 2 additions & 2 deletions packages/mui-base/src/ButtonUnstyled/buttonUnstyledClasses.ts
Expand Up @@ -11,10 +11,10 @@ export interface ButtonUnstyledClasses {
export type ButtonUnstyledClassKey = keyof ButtonUnstyledClasses;

export function getButtonUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('ButtonUnstyled', slot);
return generateUtilityClass('MuiButton', slot);
}

const buttonUnstyledClasses: ButtonUnstyledClasses = generateUtilityClasses('ButtonUnstyled', [
const buttonUnstyledClasses: ButtonUnstyledClasses = generateUtilityClasses('MuiButton', [
'root',
'active',
'disabled',
Expand Down
Expand Up @@ -19,11 +19,11 @@ export interface FormControlUnstyledClasses {
export type FormControlUnstyledClassKey = keyof FormControlUnstyledClasses;

export function getFormControlUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('BaseFormControl', slot);
return generateUtilityClass('MuiFormControl', slot);
}

const formControlUnstyledClasses: FormControlUnstyledClasses = generateUtilityClasses(
'BaseFormControl',
'MuiFormControl',
['root', 'disabled', 'error', 'filled', 'focused', 'required'],
);

Expand Down
4 changes: 2 additions & 2 deletions packages/mui-base/src/InputUnstyled/inputUnstyledClasses.ts
Expand Up @@ -32,7 +32,7 @@ export function getInputUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('MuiInput', slot);
}

const inputBaseClasses: InputUnstyledClasses = generateUtilityClasses('MuiInput', [
const inputUnstyledClasses: InputUnstyledClasses = generateUtilityClasses('MuiInput', [
'root',
'formControl',
'focused',
Expand All @@ -46,4 +46,4 @@ const inputBaseClasses: InputUnstyledClasses = generateUtilityClasses('MuiInput'
'adornedEnd',
]);

export default inputBaseClasses;
export default inputUnstyledClasses;
Expand Up @@ -10,12 +10,13 @@ export interface MenuItemUnstyledClasses {
export type MenuItemUnstyledClassKey = keyof MenuItemUnstyledClasses;

export function getMenuItemUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('MuiMenuItemUnstyled', slot);
return generateUtilityClass('MuiMenuItem', slot);
}

const menuItemUnstyledClasses: MenuItemUnstyledClasses = generateUtilityClasses(
'MuiMenuItemUnstyled',
['root', 'disabled', 'focusVisible'],
);
const menuItemUnstyledClasses: MenuItemUnstyledClasses = generateUtilityClasses('MuiMenuItem', [
'root',
'disabled',
'focusVisible',
]);

export default menuItemUnstyledClasses;
4 changes: 2 additions & 2 deletions packages/mui-base/src/MenuUnstyled/menuUnstyledClasses.ts
Expand Up @@ -10,10 +10,10 @@ export interface MenuUnstyledClasses {
export type MenuUnstyledClassKey = keyof MenuUnstyledClasses;

export function getMenuUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('MuiMenuUnstyled', slot);
return generateUtilityClass('MuiMenu', slot);
}

const menuUnstyledClasses: MenuUnstyledClasses = generateUtilityClasses('MuiMenuUnstyled', [
const menuUnstyledClasses: MenuUnstyledClasses = generateUtilityClasses('MuiMenu', [
'root',
'listbox',
'expanded',
Expand Down
Expand Up @@ -10,11 +10,11 @@ export interface OptionGroupUnstyledClasses {
export type OptionGroupUnstyledClassKey = keyof OptionGroupUnstyledClasses;

export function getOptionGroupUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('MuiOptionGroupUnstyled', slot);
return generateUtilityClass('MuiOptionGroup', slot);
}

const optionGroupUnstyledClasses: OptionGroupUnstyledClasses = generateUtilityClasses(
'MuiOptionGroupUnstyled',
'MuiOptionGroup',
['root', 'label', 'list'],
);

Expand Down
Expand Up @@ -11,10 +11,10 @@ export interface OptionUnstyledClasses {
export type OptionUnstyledClassKey = keyof OptionUnstyledClasses;

export function getOptionUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('MuiOptionUnstyled', slot);
return generateUtilityClass('MuiOption', slot);
}

const optionUnstyledClasses: OptionUnstyledClasses = generateUtilityClasses('MuiOptionUnstyled', [
const optionUnstyledClasses: OptionUnstyledClasses = generateUtilityClasses('MuiOption', [
'root',
'disabled',
'selected',
Expand Down
39 changes: 21 additions & 18 deletions packages/mui-base/src/SelectUnstyled/SelectUnstyled.test.tsx
Expand Up @@ -2,7 +2,10 @@ import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
import SelectUnstyled, { SelectOption, selectUnstyledClasses } from '@mui/base/SelectUnstyled';
import OptionUnstyled, { OptionUnstyledProps } from '@mui/base/OptionUnstyled';
import OptionUnstyled, {
OptionUnstyledProps,
optionUnstyledClasses,
} from '@mui/base/OptionUnstyled';
import OptionGroupUnstyled from '@mui/base/OptionGroupUnstyled';
import {
createMount,
Expand Down Expand Up @@ -145,11 +148,11 @@ describe('SelectUnstyled', () => {
const listbox = getByRole('listbox');

userEvent.keyPress(listbox, { key: 'd' });
expect(getByText('Dragon Fruit')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('Dragon Fruit')).to.have.class(optionUnstyledClasses.highlighted);
userEvent.keyPress(listbox, { key: 'r' });
expect(getByText('Dragon Fruit')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('Dragon Fruit')).to.have.class(optionUnstyledClasses.highlighted);
userEvent.keyPress(listbox, { key: 'z' });
expect(getByText('Dragon Fruit')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('Dragon Fruit')).to.have.class(optionUnstyledClasses.highlighted);
});

it('navigate to next element with same starting character on repeated keys', () => {
Expand All @@ -172,11 +175,11 @@ describe('SelectUnstyled', () => {
const listbox = getByRole('listbox');

userEvent.keyPress(listbox, { key: 'c' });
expect(getByText('Cherry')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('Cherry')).to.have.class(optionUnstyledClasses.highlighted);
userEvent.keyPress(listbox, { key: 'c' });
expect(getByText('Calamondin')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('Calamondin')).to.have.class(optionUnstyledClasses.highlighted);
userEvent.keyPress(listbox, { key: 'c' });
expect(getByText('Cherry')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('Cherry')).to.have.class(optionUnstyledClasses.highlighted);
});

it('navigate using the label prop', () => {
Expand Down Expand Up @@ -211,11 +214,11 @@ describe('SelectUnstyled', () => {
const listbox = getByRole('listbox');

userEvent.keyPress(listbox, { key: 'd' });
expect(getByTestId('5')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByTestId('5')).to.have.class(optionUnstyledClasses.highlighted);
userEvent.keyPress(listbox, { key: 'r' });
expect(getByTestId('5')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByTestId('5')).to.have.class(optionUnstyledClasses.highlighted);
userEvent.keyPress(listbox, { key: 'z' });
expect(getByTestId('5')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByTestId('5')).to.have.class(optionUnstyledClasses.highlighted);
});

it('skips the non-stringifiable options', () => {
Expand All @@ -241,11 +244,11 @@ describe('SelectUnstyled', () => {
const listbox = getByRole('listbox');

userEvent.keyPress(listbox, { key: 'c' });
expect(getByText('Cherry')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('Cherry')).to.have.class(optionUnstyledClasses.highlighted);
userEvent.keyPress(listbox, { key: 'c' });
expect(getByText('Calamondin')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('Calamondin')).to.have.class(optionUnstyledClasses.highlighted);
userEvent.keyPress(listbox, { key: 'c' });
expect(getByText('Cherry')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('Cherry')).to.have.class(optionUnstyledClasses.highlighted);
});

it('navigate to options with diacritic characters', () => {
Expand All @@ -267,12 +270,12 @@ describe('SelectUnstyled', () => {
const listbox = getByRole('listbox');

userEvent.keyPress(listbox, { key: 'b' });
expect(getByText('Ba')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('Ba')).to.have.class(optionUnstyledClasses.highlighted);

userEvent.keyPress(listbox, { key: 'Control' });
userEvent.keyPress(listbox, { key: 'Alt' });
userEvent.keyPress(listbox, { key: 'ą' });
expect(getByText('Bą')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('Bą')).to.have.class(optionUnstyledClasses.highlighted);
});

it('navigate to next options with beginning diacritic characters', () => {
Expand All @@ -295,17 +298,17 @@ describe('SelectUnstyled', () => {
userEvent.keyPress(listbox, { key: 'Control' });
userEvent.keyPress(listbox, { key: 'Alt' });
userEvent.keyPress(listbox, { key: 'ą' });
expect(getByText('ąa')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('ąa')).to.have.class(optionUnstyledClasses.highlighted);

userEvent.keyPress(listbox, { key: 'Alt' });
userEvent.keyPress(listbox, { key: 'Control' });
userEvent.keyPress(listbox, { key: 'ą' });
expect(getByText('ąb')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('ąb')).to.have.class(optionUnstyledClasses.highlighted);

userEvent.keyPress(listbox, { key: 'Control' });
userEvent.keyPress(listbox, { key: 'AltGraph' });
userEvent.keyPress(listbox, { key: 'ą' });
expect(getByText('ąc')).to.have.class('MuiOptionUnstyled-highlighted');
expect(getByText('ąc')).to.have.class(optionUnstyledClasses.highlighted);
});
});

Expand Down
4 changes: 2 additions & 2 deletions packages/mui-base/src/SelectUnstyled/selectUnstyledClasses.ts
Expand Up @@ -15,10 +15,10 @@ export interface SelectUnstyledClasses {
export type SelectUnstyledClassKey = keyof SelectUnstyledClasses;

export function getSelectUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('MuiSelectUnstyled', slot);
return generateUtilityClass('MuiSelect', slot);
}

const selectUnstyledClasses: SelectUnstyledClasses = generateUtilityClasses('MuiSelectUnstyled', [
const selectUnstyledClasses: SelectUnstyledClasses = generateUtilityClasses('MuiSelect', [
'root',
'button',
'listbox',
Expand Down
Expand Up @@ -9,12 +9,12 @@ export interface TabPanelUnstyledClasses {
export type TabPanelUnstyledClassKey = keyof TabPanelUnstyledClasses;

export function getTabPanelUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('TabPanelUnstyled', slot);
return generateUtilityClass('MuiTabPanel', slot);
}

const tabPanelUnstyledClasses: TabPanelUnstyledClasses = generateUtilityClasses(
'TabPanelUnstyled',
['root', 'hidden'],
);
const tabPanelUnstyledClasses: TabPanelUnstyledClasses = generateUtilityClasses('MuiTabPanel', [
'root',
'hidden',
]);

export default tabPanelUnstyledClasses;
4 changes: 2 additions & 2 deletions packages/mui-base/src/TabUnstyled/tabUnstyledClasses.ts
Expand Up @@ -10,10 +10,10 @@ export interface TabUnstyledClasses {
export type TabUnstyledClassKey = keyof TabUnstyledClasses;

export function getTabUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('TabUnstyled', slot);
return generateUtilityClass('MuiTab', slot);
}

const tabUnstyledClasses: TabUnstyledClasses = generateUtilityClasses('TabUnstyled', [
const tabUnstyledClasses: TabUnstyledClasses = generateUtilityClasses('MuiTab', [
'root',
'selected',
'disabled',
Expand Down
Expand Up @@ -29,11 +29,11 @@ export interface TablePaginationUnstyledClasses {
export type TablePaginationUnstyledClassKey = keyof TablePaginationUnstyledClasses;

export function getTablePaginationUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('MuiTablePaginationUnstyled', slot);
return generateUtilityClass('MuiTablePagination', slot);
}

const tablePaginationClasses: TablePaginationUnstyledClasses = generateUtilityClasses(
'MuiTablePaginationUnstyled',
const tablePaginationUnstyledClasses: TablePaginationUnstyledClasses = generateUtilityClasses(
'MuiTablePagination',
[
'root',
'toolbar',
Expand All @@ -49,4 +49,4 @@ const tablePaginationClasses: TablePaginationUnstyledClasses = generateUtilityCl
],
);

export default tablePaginationClasses;
export default tablePaginationUnstyledClasses;
Expand Up @@ -10,12 +10,13 @@ export interface TabsListUnstyledClasses {
export type TabsListUnstyledClassKey = keyof TabsListUnstyledClasses;

export function getTabsListUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('TabsListUnstyled', slot);
return generateUtilityClass('MuiTabsList', slot);
}

const tabsListUnstyledClasses: TabsListUnstyledClasses = generateUtilityClasses(
'TabsListUnstyled',
['root', 'horizontal', 'vertical'],
);
const tabsListUnstyledClasses: TabsListUnstyledClasses = generateUtilityClasses('MuiTabsList', [
'root',
'horizontal',
'vertical',
]);

export default tabsListUnstyledClasses;
4 changes: 2 additions & 2 deletions packages/mui-base/src/TabsUnstyled/tabsUnstyledClasses.ts
Expand Up @@ -10,10 +10,10 @@ export interface TabsUnstyledClasses {
export type TabsUnstyledClassKey = keyof TabsUnstyledClasses;

export function getTabsUnstyledUtilityClass(slot: string): string {
return generateUtilityClass('TabsUnstyled', slot);
return generateUtilityClass('MuiTabs', slot);
}

const tabsUnstyledClasses: TabsUnstyledClasses = generateUtilityClasses('TabsUnstyled', [
const tabsUnstyledClasses: TabsUnstyledClasses = generateUtilityClasses('MuiTabs', [
'root',
'horizontal',
'vertical',
Expand Down
6 changes: 0 additions & 6 deletions packages/mui-base/src/index.d.ts
Expand Up @@ -14,12 +14,6 @@ export * from './ClickAwayListener';
export { default as unstable_composeClasses } from './composeClasses';
export * from './composeClasses';

export { default as generateUtilityClass } from './generateUtilityClass';
Copy link
Member

Choose a reason for hiding this comment

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

As far as I know, the X components depend on this API. Could you double check please before removing the API?

Copy link
Member

Choose a reason for hiding this comment

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

Or they depend on the @mui/utils, not sure to be honest.

Copy link
Member Author

Choose a reason for hiding this comment

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

Their components depend on the implementation from @mui/material. But indeed there's one usage from Base, in the API docs builder. I created a PR to use the implementation from @mui/utils: mui/mui-x#6216

Copy link
Member Author

Choose a reason for hiding this comment

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

The PR in X is now merged in, so I believe this one is safe to be merged in as well.

export * from './generateUtilityClass';

export { default as generateUtilityClasses } from './generateUtilityClasses';
export * from './generateUtilityClasses';

export { default as FocusTrap } from './FocusTrap';
export * from './FocusTrap';

Expand Down
5 changes: 0 additions & 5 deletions packages/mui-base/src/index.js
Expand Up @@ -12,11 +12,6 @@ export { default as ClickAwayListener } from './ClickAwayListener';

export { default as unstable_composeClasses } from './composeClasses';

export { default as generateUtilityClass } from './generateUtilityClass';
export * from './generateUtilityClass';

export { default as generateUtilityClasses } from './generateUtilityClasses';

export { default as FocusTrap } from './FocusTrap';

export { default as FormControlUnstyled } from './FormControlUnstyled';
Expand Down
5 changes: 4 additions & 1 deletion packages/mui-joy/src/Breadcrumbs/breadcrumbsClasses.ts
@@ -1,4 +1,7 @@
import { generateUtilityClass, generateUtilityClasses } from '@mui/base';
import {
unstable_generateUtilityClass as generateUtilityClass,
unstable_generateUtilityClasses as generateUtilityClasses,
} from '@mui/utils';

Comment on lines +1 to 5
Copy link
Member

Choose a reason for hiding this comment

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

Off-topic

I don't fully remember: what's the long-term goal with@mui/utils and how do we decide which modules it owns? From what I understand, this package is used by all the others, it's a shared kitchen sink. I'm asking about it because it feels like many of the modules it has been used directly by Joy UI, Material UI, and MUI X, without going through MUI Base.

One example:

import { unstable_useId as useId } from '@mui/utils';

https://github.com/mui/mui-x/blob/3e7bfb2a3b11d3728892b245e70928c7669a0bd6/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx#L2

which makes me wonder if it would make sense to:

  1. treat @mui/utils as an alpha unstable package, private APIs
  2. reexport everything that is relevant in "@mui/base".
  3. import from "@mui/base" wherever possible.
  4. have @mui/utils host all the logic that could be required by @mui/base or @mui/system, only.

in order to better sell the value proposition of MUI Base as a set of baseline helpers to create React components, including React utils.

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 remember us deciding on the shape of this library. To me, it should be a private package (I wouldn't say "alpha" or "unstable" - it's just internal).
As for re-exporting from MUI Base - I generally agree, but only the utilities that we intend to make public. I think Material UI and Joy could still import directly from utils.

In the case of this PR, I changed the imports of generateUtilityClass, so that MUI Base could override the generic implementation from @mui/utils and add its own product name. As we ended up not using product classes, after all, this may not be necessary.

We can discuss it in the next meeting.

Copy link
Member

Choose a reason for hiding this comment

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

To me, it should be a private package (I wouldn't say "alpha" or "unstable" - it's just internal).

@michaldudak I personally prefer this option too.

I think Material UI and Joy could still import directly from utils.

Developers use the source of the components as a reference for how to create new ones. So if we bypass @mui/base in the source, I think that it would mean that @mui/utils is not an internal help for @mui/base and @mui/system but a public API they will install on their own. e.g. what MUI X is doing. But why not, we could imagine that @mui/utils is also part of MUI Base, and documented there too.

export interface BreadcrumbsClasses {
/** Styles applied to the root element. */
Expand Down
5 changes: 4 additions & 1 deletion packages/mui-joy/src/Grid/gridClasses.ts
@@ -1,4 +1,7 @@
import { generateUtilityClass, generateUtilityClasses } from '@mui/base';
import {
unstable_generateUtilityClass as generateUtilityClass,
unstable_generateUtilityClasses as generateUtilityClasses,
} from '@mui/utils';
import { GridClasses } from '@mui/system/Unstable_Grid';

export type GridClassKey = keyof GridClasses;
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-joy/src/styles/components.d.ts
@@ -1,4 +1,4 @@
import { GlobalStateSlot } from '@mui/base';
import { GlobalStateSlot } from '@mui/utils';
import { CSSInterpolation } from '@mui/system';
import {
AspectRatioProps,
Expand Down
3 changes: 2 additions & 1 deletion packages/mui-lab/src/LoadingButton/loadingButtonClasses.ts
@@ -1,4 +1,5 @@
import { generateUtilityClass, generateUtilityClasses } from '@mui/base';
import generateUtilityClass from '@mui/material/generateUtilityClass';
import generateUtilityClasses from '@mui/material/generateUtilityClasses';

export interface LoadingButtonClasses {
/** Styles applied to the root element. */
Expand Down
3 changes: 2 additions & 1 deletion packages/mui-lab/src/Masonry/masonryClasses.ts
@@ -1,4 +1,5 @@
import { generateUtilityClass, generateUtilityClasses } from '@mui/base';
import generateUtilityClass from '@mui/material/generateUtilityClass';
import generateUtilityClasses from '@mui/material/generateUtilityClasses';

export interface MasonryClasses {
/** Styles applied to the root element. */
Expand Down