Skip to content

Commit

Permalink
[styled-engine-sc][system] Add support for styled-components v6 (#3…
Browse files Browse the repository at this point in the history
  • Loading branch information
mnajdova authored Oct 9, 2023
1 parent 18d087f commit 18c3149
Show file tree
Hide file tree
Showing 17 changed files with 215 additions and 106 deletions.
2 changes: 1 addition & 1 deletion benchmark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"react-redux": "^8.1.2",
"redux": "^4.2.1",
"serve-handler": "^6.1.5",
"styled-components": "^5.3.11",
"styled-components": "^6.0.0",
"styled-system": "^5.1.5",
"theme-ui": "^0.16.1",
"webpack": "^5.88.2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ For TypeScript, you must also update the `tsconfig.json` as shown here:
+});
```

:::info
**Versions compatibility**

To ensure compatibility, it's essential to align the major version of `@mui/styled-engine-sc` with that of the `styled-components` package you're using. For instance, if you opt for `styled-components` version 5, it's necessary to use `@mui/styled-engine-sc` version 5. Similarly, if your preference is `styled-components` version 6, you'll need to upgrade `@mui/styled-engine-sc` to its version 6, which is currently in an alpha state.
:::

## Ready-to-use examples

MUI provides boilerplate examples of Create React App with Material UI and styled-components in both JavaScript and TypeScript:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import styled, { InterpolationFunction, ThemeProvider } from 'styled-components';
import styled, { ThemeProvider, StyleFunction } from 'styled-components';
import { unstable_styleFunctionSx, SxProps } from '@mui/system';
import { createTheme } from '@mui/material/styles';

Expand All @@ -10,7 +10,7 @@ interface DivProps {
const theme = createTheme();

const Div = styled('div')<DivProps>(
unstable_styleFunctionSx as InterpolationFunction<DivProps>,
unstable_styleFunctionSx as StyleFunction<DivProps>,
);

export default function StyleFunctionSxDemo() {
Expand Down
4 changes: 1 addition & 3 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,9 @@
"react-window": "^1.8.9",
"recharts": "2.8.0",
"rimraf": "^5.0.5",
"styled-components": "^5.3.11",
"styled-components": "^6.0.0",
"stylis": "4.2.0",
"stylis-plugin-rtl": "^2.1.1",
"stylis-plugin-rtl-sc": "npm:stylis-plugin-rtl@^1.1.0",
"use-count-up": "^3.0.1",
"webpack-bundle-analyzer": "^4.9.1"
},
Expand All @@ -132,7 +131,6 @@
"@types/react-swipeable-views-utils": "^0.13.5",
"@types/react-transition-group": "^4.4.7",
"@types/react-window": "^1.8.6",
"@types/styled-components": "5.1.28",
"@types/stylis": "^4.2.0",
"chai": "^4.3.10",
"cross-fetch": "^4.0.0",
Expand Down
3 changes: 1 addition & 2 deletions docs/src/modules/components/DemoSandbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
import { create } from 'jss';
import { prefixer } from 'stylis';
import rtlPlugin from 'stylis-plugin-rtl';
import rtlPluginSc from 'stylis-plugin-rtl-sc';
import createCache from '@emotion/cache';
import { CacheProvider } from '@emotion/react';
import { StyleSheetManager } from 'styled-components';
Expand Down Expand Up @@ -66,7 +65,7 @@ function FramedDemo(props) {
<StylesProvider jss={jss} sheetsManager={sheetsManager}>
<StyleSheetManager
target={document.head}
stylisPlugins={theme.direction === 'rtl' ? [rtlPluginSc] : []}
stylisPlugins={theme.direction === 'rtl' ? [rtlPlugin] : []}
>
<CacheProvider value={cache}>
<Wrapper {...wrapperProps}>
Expand Down
3 changes: 1 addition & 2 deletions docs/src/modules/utils/StyledEngineProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';
import { prefixer } from 'stylis';
import rtlPlugin from 'stylis-plugin-rtl';
import rtlPluginSc from 'stylis-plugin-rtl-sc';
import { useTheme } from '@mui/material/styles';

// Cache for the rtl version of the styles
Expand All @@ -23,7 +22,7 @@ export default function StyledEngineProvider(props) {
const emotionCache = theme.direction === 'rtl' ? cacheRtl : cacheLtr;

return (
<StyleSheetManager stylisPlugins={rtl ? [rtlPluginSc] : []}>
<StyleSheetManager stylisPlugins={rtl ? [rtlPlugin] : []}>
<CacheProvider value={emotionCache}>{children}</CacheProvider>
</StyleSheetManager>
);
Expand Down
4 changes: 4 additions & 0 deletions examples/material-ui-cra-styled-components-ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ The following link leverages this demo: https://mui.com/guides/interoperability/

This example demonstrates how to set up Material UI with [Create React App](https://github.com/facebookincubator/create-react-app) with [styled-components](https://styled-components.com/) as a style engine for your application using TypeScript.

## Versions compatibility

Note, the version 5 of `@mui/styled-engine-sc` is compatible with version 5 of `styled-components`, while the version 6 of `@mui/styled-engine-sc` (currently in alpha), is compatible with v6 of `styled-components`. When incorporating these dependencies into your project, consider this compatibility requirement. In this example application, both libraries are using version 6.

## What's next?

<!-- #default-branch-switch -->
Expand Down
3 changes: 1 addition & 2 deletions examples/material-ui-cra-styled-components-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"dependencies": {
"@mui/lab": "latest",
"@mui/material": "latest",
"@mui/styled-engine-sc": "latest",
"@mui/styled-engine-sc": "^6.0.0",
"@testing-library/jest-dom": "latest",
"@testing-library/react": "latest",
"@testing-library/user-event": "latest",
Expand Down Expand Up @@ -38,7 +38,6 @@
"@types/node": "latest",
"@types/react": "latest",
"@types/react-dom": "latest",
"@types/styled-components": "latest",
"craco-alias": "^3.0.1",
"typescript": "latest"
}
Expand Down
4 changes: 4 additions & 0 deletions examples/material-ui-cra-styled-components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ The following link leverages this demo: https://mui.com/guides/interoperability/

This example demonstrates how to set up Material UI with [Create React App](https://github.com/facebookincubator/create-react-app), using [styled-components](https://styled-components.com/) as a style engine for your application.

## Versions compatibility

Note, the version 5 of `@mui/styled-engine-sc` is compatible with version 5 of `styled-components`, while the version 6 of `@mui/styled-engine-sc` (currently in alpha), is compatible with v6 of `styled-components`. When incorporating these dependencies into your project, consider this compatibility requirement. In this example application, both libraries are using version 6.

## What's next?

<!-- #default-branch-switch -->
Expand Down
2 changes: 1 addition & 1 deletion examples/material-ui-cra-styled-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"dependencies": {
"@mui/material": "latest",
"@mui/lab": "latest",
"@mui/styled-engine-sc": "latest",
"@mui/styled-engine-sc": "^6.0.0",
"@testing-library/jest-dom": "latest",
"@testing-library/react": "latest",
"@testing-library/user-event": "latest",
Expand Down
12 changes: 3 additions & 9 deletions packages/mui-styled-engine-sc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,13 @@
"@mui-internal/test-utils": "^1.0.0",
"@types/chai": "^4.3.6",
"@types/react": "^18.2.23",
"@types/styled-components": "^5.1.28",
"chai": "^4.3.10",
"hoist-non-react-statics": "^3.3.2",
"react": "^18.2.0",
"styled-components": "^5.3.11"
"styled-components": "^6.0.0"
},
"peerDependencies": {
"@types/styled-components": "^5.1.14",
"styled-components": "^5.3.1"
},
"peerDependenciesMeta": {
"@types/styled-components": {
"optional": true
}
"styled-components": "^6.0.0"
},
"sideEffects": false,
"publishConfig": {
Expand Down
10 changes: 6 additions & 4 deletions packages/mui-styled-engine-sc/src/GlobalStyles/GlobalStyles.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as React from 'react';
import { CSSObject, InterpolationFunction } from 'styled-components';
import { CSSObject, StyleFunction } from 'styled-components';

export interface GlobalStylesProps<Theme = {}> {
export interface GlobalStylesProps<Theme extends object = {}> {
defaultTheme?: object;
styles: string | CSSObject | InterpolationFunction<Theme>;
styles: string | CSSObject | StyleFunction<Theme>;
}

export default function Global<Theme = {}>(props: GlobalStylesProps<Theme>): React.ReactElement;
export default function Global<Theme extends object = {}>(
props: GlobalStylesProps<Theme>,
): React.ReactElement;
180 changes: 168 additions & 12 deletions packages/mui-styled-engine-sc/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,68 @@
import * as React from 'react';
import * as CSS from 'csstype';
import {
AnyStyledComponent,
StyledConfig,
StyledComponent,
StyledComponentPropsWithRef,
StyledComponentInnerComponent,
StyledComponentInnerOtherProps,
StyledComponentInnerAttrs,
ThemedStyledProps,
StyledComponentBase,
Keyframes,
} from 'styled-components';
import * as hoistNonReactStatics from 'hoist-non-react-statics';

type WithOptionalTheme<P extends { theme?: T | undefined }, T> = OmitU<P, 'theme'> & {
theme?: T | undefined;
};

// Helper type operators
// Pick that distributes over union types
export type PickU<T, K extends keyof T> = T extends any ? { [P in K]: T[P] } : never;
export type OmitU<T, K extends keyof T> = T extends any ? PickU<T, Exclude<keyof T, K>> : never;

// Any prop that has a default prop becomes optional, but its type is unchanged
// Undeclared default props are augmented into the resulting allowable attributes
// If declared props have indexed properties, ignore default props entirely as keyof gets widened
// Wrap in an outer-level conditional type to allow distribution over props that are unions
type Defaultize<P, D> = P extends any
? string extends keyof P
? P
: PickU<P, Exclude<keyof P, keyof D>> &
Partial<PickU<P, Extract<keyof P, keyof D>>> &
Partial<PickU<D, Exclude<keyof D, keyof P>>>
: never;

export type IntrinsicElementsKeys = keyof JSX.IntrinsicElements;
type ReactDefaultizedProps<C, P> = C extends { defaultProps: infer D } ? Defaultize<P, D> : P;

type MakeAttrsOptional<
C extends string | React.ComponentType<any>,
O extends object,
A extends keyof P,
P = React.ComponentPropsWithRef<
C extends IntrinsicElementsKeys | React.ComponentType<any> ? C : never
>,
> =
// Distribute unions early to avoid quadratic expansion
P extends any ? OmitU<ReactDefaultizedProps<C, P> & O, A> & Partial<PickU<P & O, A>> : never;

export type StyledComponentProps<
// The Component from whose props are derived
C extends string | React.ComponentType<any>,
// The Theme from the current context
T extends object,
// The other props added by the template
O extends object,
// The props that are made optional by .attrs
A extends keyof any,
// The Component passed with "forwardedAs" prop
FAsC extends string | React.ComponentType<any> = C,
> =
// Distribute O if O is a union type
O extends object
? WithOptionalTheme<MakeAttrsOptional<C, O, A> & MakeAttrsOptional<FAsC, O, A>, T>
: never;

export interface ThemeProps<T> {
theme: T;
}

export type ThemedStyledProps<P, T> = P & ThemeProps<T>;

export interface Keyframes {
getName(): string;
}

export * from 'styled-components';
export { default } from 'styled-components';
Expand Down Expand Up @@ -83,6 +134,95 @@ export type FlattenSimpleInterpolation = ReadonlyArray<SimpleInterpolation>;

export type InterpolationFunction<P> = (props: P) => Interpolation<P>;

// abuse Pick to strip the call signature from ForwardRefExoticComponent
type ForwardRefExoticBase<P> = PickU<
React.ForwardRefExoticComponent<P>,
keyof React.ForwardRefExoticComponent<any>
>;

type StyledComponentPropsWithAs<
C extends string | React.ComponentType<any>,
T extends object,
O extends object,
A extends keyof any,
AsC extends string | React.ComponentType<any> = C,
FAsC extends string | React.ComponentType<any> = C,
> = StyledComponentProps<C, T, O, A, FAsC> & {
as?: AsC | undefined;
forwardedAs?: FAsC | undefined;
};

export type StyledComponent<
C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
T extends object,
O extends object = {},
A extends keyof any = never,
> = // the "string" allows this to be used as an object key
// I really want to avoid this if possible but it's the only way to use nesting with object styles...
string &
StyledComponentBase<C, T, O, A> &
hoistNonReactStatics.NonReactStatics<C extends React.ComponentType<any> ? C : never>;

// any doesn't count as assignable to never in the extends clause, and we default A to never
export type AnyStyledComponent =
| StyledComponent<any, any, any, any>
| StyledComponent<any, any, any>;

export type StyledComponentInnerComponent<C extends React.ComponentType<any>> =
C extends StyledComponent<infer I, any, any, any>
? I
: C extends StyledComponent<infer I, any, any>
? I
: C;

export type StyledComponentInnerOtherProps<C extends AnyStyledComponent> =
C extends StyledComponent<any, any, infer O, any>
? O
: C extends StyledComponent<any, any, infer O>
? O
: never;
export type StyledComponentInnerAttrs<C extends AnyStyledComponent> = C extends StyledComponent<
any,
any,
any,
infer A
>
? A
: never;

export interface StyledComponentBase<
C extends string | React.ComponentType<any>,
T extends object,
O extends object = {},
A extends keyof any = never,
> extends ForwardRefExoticBase<StyledComponentProps<C, T, O, A>> {
// add our own fake call signature to implement the polymorphic 'as' prop
(
props: StyledComponentProps<C, T, O, A> & {
as?: never | undefined;
forwardedAs?: never | undefined;
},
): React.ReactElement<StyledComponentProps<C, T, O, A>>;
<
AsC extends string | React.ComponentType<any> = C,
FAsC extends string | React.ComponentType<any> = AsC,
>(
props: StyledComponentPropsWithAs<AsC, T, O, A, AsC, FAsC>,
): React.ReactElement<StyledComponentPropsWithAs<AsC, T, O, A, AsC, FAsC>>;

withComponent<WithC extends AnyStyledComponent>(
component: WithC,
): StyledComponent<
StyledComponentInnerComponent<WithC>,
T,
O & StyledComponentInnerOtherProps<WithC>,
A | StyledComponentInnerAttrs<WithC>
>;
withComponent<WithC extends keyof JSX.IntrinsicElements | React.ComponentType<any>>(
component: WithC,
): StyledComponent<WithC, T, O, A>;
}

// remove the call signature from StyledComponent so Interpolation can still infer InterpolationFunction
type StyledComponentInterpolation =
| Pick<StyledComponentBase<any, any, any, any>, keyof StyledComponentBase<any, any>>
Expand All @@ -96,6 +236,12 @@ type ThemedStyledComponentFactories<T extends object> = {
[TTag in keyof JSX.IntrinsicElements]: ThemedStyledFunctionBase<TTag, T>;
};

export type StyledComponentPropsWithRef<
C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
> = C extends AnyStyledComponent
? React.ComponentPropsWithRef<StyledComponentInnerComponent<C>>
: React.ComponentPropsWithRef<C>;

// Same as in styled-components, but copied here so that it would use the Interpolation & CSS typings from above
export interface ThemedStyledFunctionBase<
C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
Expand Down Expand Up @@ -128,6 +274,16 @@ export interface ThemedStyledFunction<
A extends keyof any = never,
> extends ThemedStyledFunctionBase<C, T, O, A> {}

// Config to be used with withConfig
export interface StyledConfig<O extends object = {}> {
// TODO: Add all types from the original StyledComponentWrapperProperties
componentId?: string;
displayName?: string;
shouldForwardProp?:
| ((prop: keyof O, defaultValidatorFn: (prop: keyof O) => boolean) => boolean)
| undefined;
}

// same as ThemedBaseStyledInterface in styled-components, but with added options & common props for MUI components
export interface ThemedBaseStyledInterface<
MUIStyledCommonProps extends object,
Expand Down
Loading

0 comments on commit 18c3149

Please sign in to comment.