mirror of
https://github.com/gosticks/DefinitelyTyped.git
synced 2026-07-01 15:50:13 +00:00
Merge pull request #30511 from Kovensky/styled-components-cssobject
styled-components: Add more complete support for object styles, tests
This commit is contained in:
239
types/styled-components/index.d.ts
vendored
239
types/styled-components/index.d.ts
vendored
@@ -9,17 +9,28 @@
|
||||
|
||||
/// <reference types="node" />
|
||||
|
||||
import * as React from 'react';
|
||||
import * as CSS from 'csstype';
|
||||
import * as CSS from "csstype";
|
||||
import * as React from "react";
|
||||
|
||||
export type CSSObject = CSS.Properties<string | number>;
|
||||
export type CSSObject = CSS.Properties<string | number> &
|
||||
// Index type to allow selector nesting
|
||||
// This is "[key in string]" and not "[key: string]" to allow CSSObject to be self-referential
|
||||
{
|
||||
// we need the CSS.Properties in here too to ensure the index signature doesn't create impossible values
|
||||
[key in string]:
|
||||
| CSS.Properties<string | number>[keyof CSS.Properties<
|
||||
string | number
|
||||
>]
|
||||
| CSSObject
|
||||
};
|
||||
export type CSSKeyframes = object & { [key: string]: CSSObject };
|
||||
|
||||
export interface ThemeProps<T> {
|
||||
theme: T;
|
||||
}
|
||||
|
||||
export type ThemedStyledProps<P, T> = P & ThemeProps<T>;
|
||||
export type StyledProps<P> = ThemedStyledProps<P, any>;
|
||||
export type StyledProps<P> = ThemedStyledProps<P, AnyIfEmpty<DefaultTheme>>;
|
||||
|
||||
export type StyledComponentProps<
|
||||
// The Component from whose props are derived
|
||||
@@ -45,27 +56,25 @@ type StyledComponentPropsWithAs<
|
||||
|
||||
export type FalseyValue = undefined | null | false;
|
||||
export type Interpolation<P> =
|
||||
| FlattenInterpolation<P>
|
||||
| ReadonlyArray<
|
||||
FlattenInterpolation<P> | ReadonlyArray<FlattenInterpolation<P>>
|
||||
>;
|
||||
export type FlattenInterpolation<P> =
|
||||
| InterpolationValue
|
||||
| FlattenInterpolation<P>
|
||||
| InterpolationFunction<P>;
|
||||
// must be an interface to be self-referential
|
||||
export interface FlattenInterpolation<P>
|
||||
extends ReadonlyArray<Interpolation<P>> {}
|
||||
export type InterpolationValue =
|
||||
| string
|
||||
| number
|
||||
| Styles
|
||||
| FalseyValue
|
||||
| Keyframes
|
||||
| StyledComponentInterpolation
|
||||
| CSSObject;
|
||||
export type SimpleInterpolation =
|
||||
| InterpolationValue
|
||||
| ReadonlyArray<InterpolationValue | ReadonlyArray<InterpolationValue>>;
|
||||
export interface Styles {
|
||||
[ruleOrSelector: string]: string | number | Styles;
|
||||
}
|
||||
| FlattenSimpleInterpolation;
|
||||
// must be an interface to be self-referential
|
||||
interface FlattenSimpleInterpolation
|
||||
extends ReadonlyArray<SimpleInterpolation> {}
|
||||
|
||||
export type InterpolationFunction<P> = (props: P) => Interpolation<P>;
|
||||
|
||||
@@ -76,19 +85,23 @@ type DeprecatedAttrs<P, A extends Partial<P>, T> = {
|
||||
[K in keyof A]: ((props: ThemedStyledProps<P, T>) => A[K]) | A[K]
|
||||
};
|
||||
|
||||
export type ThemedGlobalStyledClassProps<P, T> = P & {
|
||||
suppressMultiMountWarning?: boolean
|
||||
theme?: T
|
||||
export type ThemedGlobalStyledClassProps<P, T> = WithOptionalTheme<P, T> & {
|
||||
suppressMultiMountWarning?: boolean;
|
||||
};
|
||||
|
||||
export interface GlobalStyleComponent<P, T>
|
||||
extends React.ComponentClass<ThemedGlobalStyledClassProps<P, T>> {}
|
||||
|
||||
// remove the call signature from StyledComponent so Interpolation can still infer InterpolationFunction
|
||||
type StyledComponentInterpolation = Pick<
|
||||
StyledComponent<any, any>,
|
||||
keyof StyledComponent<any, any>
|
||||
>;
|
||||
type StyledComponentInterpolation =
|
||||
| Pick<
|
||||
StyledComponentBase<any, any, any, any>,
|
||||
keyof StyledComponentBase<any, any>
|
||||
>
|
||||
| Pick<
|
||||
StyledComponentBase<any, any, any>,
|
||||
keyof StyledComponentBase<any, any>
|
||||
>;
|
||||
|
||||
// abuse Pick to strip the call signature from ForwardRefExoticComponent
|
||||
type ForwardRefExoticBase<P> = Pick<
|
||||
@@ -101,7 +114,16 @@ export type AnyStyledComponent =
|
||||
| StyledComponent<any, any, any, any>
|
||||
| StyledComponent<any, any, any>;
|
||||
|
||||
export interface StyledComponent<
|
||||
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>;
|
||||
|
||||
export interface StyledComponentBase<
|
||||
C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
|
||||
T extends object,
|
||||
O extends object = {},
|
||||
@@ -127,7 +149,7 @@ export interface StyledComponent<
|
||||
*
|
||||
* String types need to be cast to themselves to become literal types (as={'a' as 'a'}).
|
||||
*/
|
||||
as?: keyof JSX.IntrinsicElements | React.ComponentType<any>
|
||||
as?: keyof JSX.IntrinsicElements | React.ComponentType<any>;
|
||||
}
|
||||
): React.ReactElement<StyledComponentProps<C, T, O, A>>;
|
||||
withComponent<WithC extends AnyStyledComponent>(
|
||||
@@ -145,49 +167,73 @@ export interface StyledComponent<
|
||||
): StyledComponent<WithC, T, O, A>;
|
||||
}
|
||||
|
||||
export interface ThemedStyledFunction<
|
||||
export interface ThemedStyledFunctionBase<
|
||||
C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
|
||||
T extends object,
|
||||
O extends object = {},
|
||||
A extends keyof any = never
|
||||
> {
|
||||
(
|
||||
strings: TemplateStringsArray,
|
||||
...interpolations: Array<
|
||||
first:
|
||||
| TemplateStringsArray
|
||||
| NonNullable<
|
||||
Interpolation<
|
||||
ThemedStyledProps<StyledComponentPropsWithRef<C> & O, T>
|
||||
>
|
||||
>,
|
||||
...rest: Array<
|
||||
Interpolation<
|
||||
ThemedStyledProps<StyledComponentPropsWithRef<C> & O, T>
|
||||
>
|
||||
>
|
||||
): StyledComponent<C, T, O, A>;
|
||||
// at least the first argument is required, whatever it is
|
||||
<U extends object>(
|
||||
strings: TemplateStringsArray,
|
||||
...interpolations: Array<
|
||||
first:
|
||||
| TemplateStringsArray
|
||||
| NonNullable<
|
||||
Interpolation<
|
||||
ThemedStyledProps<
|
||||
StyledComponentPropsWithRef<C> & O & U,
|
||||
T
|
||||
>
|
||||
>
|
||||
>,
|
||||
...rest: Array<
|
||||
Interpolation<
|
||||
ThemedStyledProps<StyledComponentPropsWithRef<C> & O & U, T>
|
||||
>
|
||||
>
|
||||
): StyledComponent<C, T, O & U, A>;
|
||||
}
|
||||
|
||||
export interface ThemedStyledFunction<
|
||||
C extends keyof JSX.IntrinsicElements | React.ComponentType<any>,
|
||||
T extends object,
|
||||
O extends object = {},
|
||||
A extends keyof any = never
|
||||
> extends ThemedStyledFunctionBase<C, T, O, A> {
|
||||
// Fun thing: 'attrs' can also provide a polymorphic 'as' prop
|
||||
// My head already hurts enough so maybe later...
|
||||
attrs<
|
||||
U,
|
||||
A extends Partial<StyledComponentPropsWithRef<C> & U> & {
|
||||
[others: string]: any
|
||||
NewA extends Partial<StyledComponentPropsWithRef<C> & U> & {
|
||||
[others: string]: any;
|
||||
} = {}
|
||||
>(
|
||||
attrs: Attrs<StyledComponentPropsWithRef<C> & U, A, T>
|
||||
): ThemedStyledFunction<C, T, O & A, keyof A>;
|
||||
attrs: Attrs<StyledComponentPropsWithRef<C> & U, NewA, T>
|
||||
): ThemedStyledFunction<C, T, O & NewA, A | keyof NewA>;
|
||||
// Only this overload is deprecated
|
||||
// tslint:disable:unified-signatures
|
||||
/** @deprecated Prefer using the new single function style, to be removed in v5 */
|
||||
attrs<
|
||||
U,
|
||||
A extends Partial<StyledComponentPropsWithRef<C> & U> & {
|
||||
[others: string]: any
|
||||
NewA extends Partial<StyledComponentPropsWithRef<C> & U> & {
|
||||
[others: string]: any;
|
||||
} = {}
|
||||
>(
|
||||
attrs: DeprecatedAttrs<StyledComponentPropsWithRef<C> & U, A, T>
|
||||
): ThemedStyledFunction<C, T, O & A, keyof A>;
|
||||
attrs: DeprecatedAttrs<StyledComponentPropsWithRef<C> & U, NewA, T>
|
||||
): ThemedStyledFunction<C, T, O & NewA, A | keyof NewA>;
|
||||
// tslint:enable:unified-signatures
|
||||
}
|
||||
|
||||
@@ -222,17 +268,13 @@ type StyledComponentInnerAttrs<
|
||||
|
||||
export interface ThemedBaseStyledInterface<T extends object>
|
||||
extends ThemedStyledComponentFactories<T> {
|
||||
<TTag extends keyof JSX.IntrinsicElements>(tag: TTag): ThemedStyledFunction<
|
||||
TTag,
|
||||
T
|
||||
>;
|
||||
<C extends AnyStyledComponent>(component: C): ThemedStyledFunction<
|
||||
StyledComponentInnerComponent<C>,
|
||||
T,
|
||||
StyledComponentInnerOtherProps<C>,
|
||||
StyledComponentInnerAttrs<C>
|
||||
>;
|
||||
<C extends React.ComponentType<any>>(
|
||||
<C extends keyof JSX.IntrinsicElements | React.ComponentType<any>>(
|
||||
// unfortunately using a conditional type to validate that it can receive a `theme?: Theme`
|
||||
// causes tests to fail in TS 3.1
|
||||
component: C
|
||||
@@ -240,60 +282,67 @@ export interface ThemedBaseStyledInterface<T extends object>
|
||||
}
|
||||
|
||||
export type ThemedStyledInterface<T extends object> = ThemedBaseStyledInterface<
|
||||
Extract<keyof T, string> extends never ? any : T
|
||||
AnyIfEmpty<T>
|
||||
>;
|
||||
export type StyledInterface = ThemedStyledInterface<DefaultTheme>;
|
||||
|
||||
export interface BaseThemedCssFunction<T extends object> {
|
||||
(cssObject: CSSObject): InterpolationValue[];
|
||||
(
|
||||
strings: TemplateStringsArray,
|
||||
...interpolations: SimpleInterpolation[]
|
||||
): InterpolationValue[];
|
||||
<P>(
|
||||
strings: TemplateStringsArray,
|
||||
first:
|
||||
| TemplateStringsArray
|
||||
| NonNullable<Interpolation<ThemedStyledProps<{}, T>>>,
|
||||
...interpolations: Array<Interpolation<ThemedStyledProps<{}, T>>>
|
||||
): FlattenInterpolation<ThemedStyledProps<{}, T>>;
|
||||
<P extends object>(
|
||||
first:
|
||||
| TemplateStringsArray
|
||||
| NonNullable<Interpolation<ThemedStyledProps<P, T>>>,
|
||||
...interpolations: Array<Interpolation<ThemedStyledProps<P, T>>>
|
||||
): Array<FlattenInterpolation<ThemedStyledProps<P, T>>>;
|
||||
<P>(func: InterpolationFunction<ThemedStyledProps<P, T>>): Array<
|
||||
FlattenInterpolation<ThemedStyledProps<P, T>>
|
||||
>;
|
||||
): FlattenInterpolation<ThemedStyledProps<P, T>>;
|
||||
}
|
||||
|
||||
export type ThemedCssFunction<T extends object> = BaseThemedCssFunction<
|
||||
Extract<keyof T, string> extends never ? any : T
|
||||
AnyIfEmpty<T>
|
||||
>;
|
||||
|
||||
// Helper type operators
|
||||
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
||||
type DiffBetween<T, U> = Pick<T, Exclude<keyof T, keyof U>> &
|
||||
Pick<U, Exclude<keyof U, keyof T>>;
|
||||
type WithOptionalTheme<P extends { theme?: T }, T> = Omit<P, 'theme'> & {
|
||||
theme?: T
|
||||
type WithOptionalTheme<P extends { theme?: T }, T> = Omit<P, "theme"> & {
|
||||
theme?: T;
|
||||
};
|
||||
type AnyIfEmpty<T extends object> = keyof T extends never ? any : T;
|
||||
|
||||
export interface ThemedStyledComponentsModule<T extends object> {
|
||||
export interface ThemedStyledComponentsModule<
|
||||
T extends object,
|
||||
U extends object = T
|
||||
> {
|
||||
default: ThemedStyledInterface<T>;
|
||||
|
||||
css: ThemedCssFunction<T>;
|
||||
|
||||
keyframes(cssObject: CSSObject): Keyframes;
|
||||
// unfortunately keyframes can't interpolate props from the theme
|
||||
keyframes(
|
||||
strings: TemplateStringsArray,
|
||||
strings: TemplateStringsArray | CSSKeyframes,
|
||||
...interpolations: SimpleInterpolation[]
|
||||
): Keyframes;
|
||||
|
||||
createGlobalStyle(cssObject: CSSObject): GlobalStyleComponent<{}, T>;
|
||||
createGlobalStyle<P = {}>(
|
||||
strings: TemplateStringsArray,
|
||||
createGlobalStyle<P extends object = {}>(
|
||||
first:
|
||||
| TemplateStringsArray
|
||||
| NonNullable<Interpolation<ThemedStyledProps<P, T>>>,
|
||||
...interpolations: Array<Interpolation<ThemedStyledProps<P, T>>>
|
||||
): GlobalStyleComponent<P, T>;
|
||||
createGlobalStyle<P = {}>(
|
||||
func: InterpolationFunction<ThemedStyledProps<P, T>>
|
||||
): GlobalStyleComponent<P, T>;
|
||||
|
||||
withTheme: WithThemeFnInterface<T>;
|
||||
ThemeProvider: ThemeProviderComponent<T>;
|
||||
ThemeProvider: ThemeProviderComponent<T, U>;
|
||||
ThemeConsumer: React.Consumer<T>;
|
||||
ThemeContext: React.Context<T>;
|
||||
|
||||
// This could be made to assert `target is StyledComponent<any, T>` instead, but that feels not type safe
|
||||
isStyledComponent: typeof isStyledComponent;
|
||||
|
||||
ServerStyleSheet: typeof ServerStyleSheet;
|
||||
StyleSheetManager: typeof StyleSheetManager;
|
||||
}
|
||||
|
||||
declare const styled: StyledInterface;
|
||||
@@ -310,49 +359,51 @@ export type BaseWithThemeFnInterface<T extends object> = <
|
||||
WithOptionalTheme<React.ComponentPropsWithRef<C>, T>
|
||||
>;
|
||||
export type WithThemeFnInterface<T extends object> = BaseWithThemeFnInterface<
|
||||
Extract<keyof T, string> extends never ? any : T
|
||||
AnyIfEmpty<T>
|
||||
>;
|
||||
export const withTheme: WithThemeFnInterface<DefaultTheme>;
|
||||
|
||||
/**
|
||||
* This interface can be augmented by users to add types to `styled-components`' default theme
|
||||
* without needing to reexport `ThemedStyledComponentsModule`.
|
||||
*/
|
||||
// Unfortunately, there is no way to write tests for this
|
||||
// as any augmentation will break the tests for the default case (not augmented).
|
||||
// tslint:disable-next-line:no-empty-interface
|
||||
export interface DefaultTheme {}
|
||||
|
||||
export interface ThemeProviderProps<T extends object> {
|
||||
export interface ThemeProviderProps<T extends object, U extends object = T> {
|
||||
children?: React.ReactChild; // only one child is allowed, goes through React.Children.only
|
||||
theme: T | ((theme: T) => T);
|
||||
theme: T | ((theme: U) => T);
|
||||
}
|
||||
export type BaseThemeProviderComponent<T extends object> = React.ComponentClass<
|
||||
ThemeProviderProps<T>
|
||||
>;
|
||||
export type BaseThemeProviderComponent<
|
||||
T extends object,
|
||||
U extends object = T
|
||||
> = React.ComponentClass<ThemeProviderProps<T, U>>;
|
||||
export type ThemeProviderComponent<
|
||||
T extends object
|
||||
> = BaseThemeProviderComponent<Extract<keyof T, string> extends never ? any : T>;
|
||||
export const ThemeProvider: ThemeProviderComponent<DefaultTheme>;
|
||||
// NOTE: this technically starts as undefined
|
||||
// Also, this cannot be DefaultTheme as it breaks TypedComponents' assignability
|
||||
export const ThemeContext: React.Context<any>;
|
||||
export const ThemeConsumer: typeof ThemeContext['Consumer'];
|
||||
T extends object,
|
||||
U extends object = T
|
||||
> = BaseThemeProviderComponent<AnyIfEmpty<T>, AnyIfEmpty<U>>;
|
||||
export const ThemeProvider: ThemeProviderComponent<AnyIfEmpty<DefaultTheme>>;
|
||||
// NOTE: this technically starts as undefined, but allowing undefined is unhelpful when used correctly
|
||||
export const ThemeContext: React.Context<AnyIfEmpty<DefaultTheme>>;
|
||||
export const ThemeConsumer: typeof ThemeContext["Consumer"];
|
||||
|
||||
export interface Keyframes {
|
||||
getName(): string;
|
||||
}
|
||||
|
||||
export function keyframes(cssObject: CSSObject): Keyframes;
|
||||
export function keyframes(
|
||||
strings: TemplateStringsArray,
|
||||
strings: TemplateStringsArray | CSSKeyframes,
|
||||
...interpolations: SimpleInterpolation[]
|
||||
): Keyframes;
|
||||
|
||||
export function createGlobalStyle(
|
||||
cssObject: CSSObject
|
||||
): GlobalStyleComponent<{}, DefaultTheme>;
|
||||
export function createGlobalStyle<P = {}>(
|
||||
strings: TemplateStringsArray,
|
||||
export function createGlobalStyle<P extends object = {}>(
|
||||
first:
|
||||
| TemplateStringsArray
|
||||
| NonNullable<Interpolation<ThemedStyledProps<P, DefaultTheme>>>,
|
||||
...interpolations: Array<Interpolation<ThemedStyledProps<P, DefaultTheme>>>
|
||||
): GlobalStyleComponent<P, DefaultTheme>;
|
||||
export function createGlobalStyle<P = {}>(
|
||||
func: InterpolationFunction<ThemedStyledProps<P, DefaultTheme>>
|
||||
): GlobalStyleComponent<P, DefaultTheme>;
|
||||
|
||||
export function isStyledComponent(
|
||||
target: any
|
||||
@@ -373,12 +424,12 @@ export class ServerStyleSheet {
|
||||
|
||||
type StyleSheetManagerProps =
|
||||
| {
|
||||
sheet: ServerStyleSheet
|
||||
target?: never
|
||||
sheet: ServerStyleSheet;
|
||||
target?: never;
|
||||
}
|
||||
| {
|
||||
sheet?: never
|
||||
target: HTMLElement
|
||||
sheet?: never;
|
||||
target: HTMLElement;
|
||||
};
|
||||
|
||||
export class StyleSheetManager extends React.Component<
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import * as ReactDOMServer from 'react-dom/server';
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import * as ReactDOMServer from "react-dom/server";
|
||||
|
||||
import styled, {
|
||||
css,
|
||||
@@ -14,7 +14,8 @@ import styled, {
|
||||
withTheme,
|
||||
ThemeConsumer,
|
||||
StyledComponent,
|
||||
} from 'styled-components';
|
||||
ThemedStyledComponentsModule
|
||||
} from "styled-components";
|
||||
|
||||
/**
|
||||
* general usage
|
||||
@@ -72,8 +73,8 @@ const TomatoButton = styled(MyButton)`
|
||||
|
||||
const CustomizableButton = styled(MyButton)`
|
||||
/* Adapt the colors based on primary prop */
|
||||
background: ${props => (props.primary ? 'palevioletred' : 'white')};
|
||||
color: ${props => (props.primary ? 'white' : 'palevioletred')};
|
||||
background: ${props => (props.primary ? "palevioletred" : "white")};
|
||||
color: ${props => (props.primary ? "white" : "palevioletred")};
|
||||
|
||||
font-size: 1em;
|
||||
margin: 1em;
|
||||
@@ -86,7 +87,7 @@ const example = css`
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
color: ${props => props.theme.primary};
|
||||
border-color: ${'red'};
|
||||
border-color: ${"red"};
|
||||
`;
|
||||
|
||||
const fadeIn = keyframes`
|
||||
@@ -99,15 +100,15 @@ const fadeIn = keyframes`
|
||||
`;
|
||||
|
||||
const animationRule = css`
|
||||
${fadeIn} 1s infinite alternate;
|
||||
${fadeIn} 1s infinite alternate;
|
||||
`;
|
||||
|
||||
const ComponentWithKeyframe = styled.div`
|
||||
animation: ${animationRule};
|
||||
animation: ${animationRule};
|
||||
`;
|
||||
|
||||
const theme = {
|
||||
main: 'mediumseagreen',
|
||||
main: "mediumseagreen"
|
||||
};
|
||||
|
||||
const ExampleGlobalStyle = createGlobalStyle`
|
||||
@@ -130,7 +131,7 @@ class Example extends React.Component {
|
||||
<Wrapper>
|
||||
<Title>
|
||||
Hello World, this is my first styled component!
|
||||
</Title>
|
||||
</Title>
|
||||
|
||||
<Input placeholder="@mxstbr" type="text" />
|
||||
<TomatoButton name="demo" />
|
||||
@@ -143,13 +144,13 @@ class Example extends React.Component {
|
||||
|
||||
// css which only uses simple interpolations without functions
|
||||
const cssWithValues1 = css`
|
||||
font-size: ${14} ${'pt'};
|
||||
font-size: ${14} ${"pt"};
|
||||
`;
|
||||
// css which uses other simple interpolations without functions
|
||||
const cssWithValues2 = css`
|
||||
${cssWithValues1}
|
||||
${[cssWithValues1, cssWithValues1]}
|
||||
font-weight: ${'bold'};
|
||||
font-weight: ${"bold"};
|
||||
`;
|
||||
|
||||
// css which uses function interpolations with common props
|
||||
@@ -168,7 +169,7 @@ const styledButton = styled.button`
|
||||
${() => [cssWithFunc1, cssWithFunc2]}
|
||||
`;
|
||||
|
||||
const name = 'hey';
|
||||
const name = "hey";
|
||||
|
||||
const ThemedMyButton = withTheme(MyButton);
|
||||
|
||||
@@ -216,7 +217,7 @@ const ComposedLink = () => (
|
||||
|
||||
// Create a <LinkFromString> react component that renders an <a> which is
|
||||
// centered, palevioletred and sized at 1.5em
|
||||
const LinkFromString = styled('a')`
|
||||
const LinkFromString = styled("a")`
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
color: palevioletred;
|
||||
@@ -235,10 +236,10 @@ interface LinkProps {
|
||||
canClick: boolean;
|
||||
}
|
||||
|
||||
const LinkFromStringWithProps = styled('a')`
|
||||
const LinkFromStringWithProps = styled("a")`
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
color: ${(a: LinkProps) => (a.canClick ? 'palevioletred' : 'gray')};
|
||||
color: ${(a: LinkProps) => (a.canClick ? "palevioletred" : "gray")};
|
||||
`;
|
||||
|
||||
// A LinkFromStringWithProps instance should be backed by an HTMLAnchorElement
|
||||
@@ -251,10 +252,10 @@ const MyOtherComponentWithProps = () => (
|
||||
|
||||
// Create a <LinkFromStringWithPropsAndGenerics> react component that renders an <a>
|
||||
// which takes extra props passed as a generic type argument
|
||||
const LinkFromStringWithPropsAndGenerics = styled('a')<LinkProps>`
|
||||
const LinkFromStringWithPropsAndGenerics = styled("a")<LinkProps>`
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
color: ${a => (a.canClick ? 'palevioletred' : 'gray')};
|
||||
color: ${a => (a.canClick ? "palevioletred" : "gray")};
|
||||
`;
|
||||
|
||||
// A LinkFromStringWithPropsAndGenerics instance should be backed by an HTMLAnchorElement
|
||||
@@ -274,19 +275,19 @@ interface ObjectStyleProps {
|
||||
}
|
||||
|
||||
const functionReturningStyleObject = (props: ObjectStyleProps) => ({
|
||||
padding: props.size === 'big' ? '10px' : 2,
|
||||
padding: props.size === "big" ? "10px" : 2
|
||||
});
|
||||
|
||||
const ObjectStylesBox = styled.div`
|
||||
${functionReturningStyleObject} ${{
|
||||
backgroundColor: 'red',
|
||||
backgroundColor: "red",
|
||||
|
||||
// Supports nested objects (pseudo selectors, media queries, etc)
|
||||
'@media screen and (min-width: 800px)': {
|
||||
backgroundColor: 'blue',
|
||||
"@media screen and (min-width: 800px)": {
|
||||
backgroundColor: "blue"
|
||||
},
|
||||
|
||||
fontSize: 2,
|
||||
fontSize: 2
|
||||
}};
|
||||
`;
|
||||
|
||||
@@ -298,11 +299,11 @@ const ObjectStylesBox = styled.div`
|
||||
|
||||
const AttrsInput = styled.input.attrs({
|
||||
// we can define static props
|
||||
type: 'password',
|
||||
type: "password",
|
||||
|
||||
// or we can define dynamic ones
|
||||
margin: (props: any) => (props.size as string) || '1em',
|
||||
padding: (props: any) => (props.size as string) || '1em',
|
||||
margin: (props: any) => (props.size as string) || "1em",
|
||||
padding: (props: any) => (props.size as string) || "1em"
|
||||
})`
|
||||
color: palevioletred;
|
||||
font-size: 1em;
|
||||
@@ -315,11 +316,15 @@ const AttrsInput = styled.input.attrs({
|
||||
`;
|
||||
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30042
|
||||
const AttrsWithOnlyNewProps = styled.h2.attrs({ as: 'h1' })`
|
||||
color: ${props => props.as === 'h1' ? 'red' : 'blue'};
|
||||
font-size: ${props => props.as === 'h1' ? 2 : 1};
|
||||
const AttrsWithOnlyNewProps = styled.h2.attrs({ as: "h1" })`
|
||||
color: ${props => (props.as === "h1" ? "red" : "blue")};
|
||||
font-size: ${props => (props.as === "h1" ? 2 : 1)};
|
||||
`;
|
||||
|
||||
const AttrsInputExtra = styled(AttrsInput).attrs({ autoComplete: "off" })``;
|
||||
|
||||
<AttrsInputExtra />;
|
||||
|
||||
/**
|
||||
* component type
|
||||
*/
|
||||
@@ -356,14 +361,14 @@ const ThemedButton = styled.button`
|
||||
|
||||
// Define our `fg` and `bg` on the theme
|
||||
const theme2 = {
|
||||
fg: 'palevioletred',
|
||||
bg: 'white',
|
||||
fg: "palevioletred",
|
||||
bg: "white"
|
||||
};
|
||||
|
||||
// This theme swaps `fg` and `bg`
|
||||
const invertTheme = ({ fg, bg }: { fg: string; bg: string }) => ({
|
||||
fg: bg,
|
||||
bg: fg,
|
||||
bg: fg
|
||||
});
|
||||
|
||||
const MyApp = (
|
||||
@@ -386,7 +391,7 @@ class MyComponent extends React.Component<ThemeProps<{}>> {
|
||||
render() {
|
||||
const { theme } = this.props;
|
||||
|
||||
console.log('Current theme: ', theme);
|
||||
console.log("Current theme: ", theme);
|
||||
|
||||
return <h1>Hello</h1>;
|
||||
}
|
||||
@@ -400,7 +405,7 @@ const ThemedMyComponent = withTheme(MyComponent);
|
||||
// ref;
|
||||
// }}/>;
|
||||
const themedRef = React.createRef<MyComponent>();
|
||||
<ThemedMyComponent ref={themedRef}/>;
|
||||
<ThemedMyComponent ref={themedRef} />;
|
||||
|
||||
interface WithThemeProps {
|
||||
theme: {
|
||||
@@ -415,12 +420,10 @@ const Component = (props: WithThemeProps) => (
|
||||
|
||||
const ComponentWithTheme = withTheme(Component);
|
||||
|
||||
<ComponentWithTheme text={'hi'} />; // ok
|
||||
<ComponentWithTheme text={'hi'} theme={{ color: 'red' }} />; // ok
|
||||
<ComponentWithTheme text={"hi"} />; // ok
|
||||
<ComponentWithTheme text={"hi"} theme={{ color: "red" }} />; // ok
|
||||
|
||||
<ThemeConsumer>
|
||||
{(theme) => <Component text="hi" theme={theme} />}
|
||||
</ThemeConsumer>;
|
||||
<ThemeConsumer>{theme => <Component text="hi" theme={theme} />}</ThemeConsumer>;
|
||||
|
||||
/**
|
||||
* isStyledComponent utility
|
||||
@@ -439,7 +442,7 @@ class ClassComponent extends React.Component {
|
||||
isStyledComponent(StyledComponent);
|
||||
isStyledComponent(StatelessComponent);
|
||||
isStyledComponent(ClassComponent);
|
||||
isStyledComponent('div');
|
||||
isStyledComponent("div");
|
||||
|
||||
/**
|
||||
* server side rendering
|
||||
@@ -470,7 +473,7 @@ const css2 = sheet2.getStyleElement();
|
||||
const sheet3 = new ServerStyleSheet();
|
||||
const appStream = ReactDOMServer.renderToNodeStream(<Title>Hello world</Title>);
|
||||
const wrappedCssStream: NodeJS.ReadableStream = sheet3.interleaveWithNodeStream(
|
||||
appStream,
|
||||
appStream
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -511,10 +514,10 @@ class Random extends React.Component<any, any> {
|
||||
}
|
||||
}
|
||||
|
||||
const WithComponentH2 = WithComponentH1.withComponent('h2');
|
||||
const WithComponentAbbr = WithComponentH1.withComponent('abbr');
|
||||
const WithComponentH2 = WithComponentH1.withComponent("h2");
|
||||
const WithComponentAbbr = WithComponentH1.withComponent("abbr");
|
||||
|
||||
const WithComponentAnchor = WithComponentH1.withComponent('a');
|
||||
const WithComponentAnchor = WithComponentH1.withComponent("a");
|
||||
const AnchorContainer = () => (
|
||||
<WithComponentAnchor href="https://example.com">
|
||||
withComponent Anchor
|
||||
@@ -524,37 +527,41 @@ const AnchorContainer = () => (
|
||||
const WithComponentRandomHeading = WithComponentH1.withComponent(Random);
|
||||
|
||||
const WithComponentCompA: React.SFC<{ a: number; className?: string }> = ({
|
||||
className,
|
||||
className
|
||||
}) => <div className={className} />;
|
||||
const WithComponentCompB: React.SFC<{ b: number; className?: string }> = ({
|
||||
className,
|
||||
className
|
||||
}) => <div className={className} />;
|
||||
const WithComponentStyledA = styled(WithComponentCompA)`
|
||||
color: ${(props: { color: string }) => props.color};
|
||||
`;
|
||||
|
||||
const WithComponentFirstStyledA = styled(WithComponentStyledA).attrs({
|
||||
a: 1,
|
||||
a: 1
|
||||
})``;
|
||||
|
||||
const WithComponentFirstStyledB = WithComponentFirstStyledA.withComponent(
|
||||
WithComponentCompB,
|
||||
WithComponentCompB
|
||||
);
|
||||
|
||||
const WithComponentFirstStyledANew = styled(WithComponentStyledA).attrs(props => ({ a: 1 }))``;
|
||||
const WithComponentFirstStyledANew = styled(WithComponentStyledA).attrs(
|
||||
props => ({ a: 1 })
|
||||
)``;
|
||||
|
||||
const test = () => [
|
||||
<WithComponentFirstStyledA color={'black'} />,
|
||||
<WithComponentFirstStyledB b={2} color={'black'} />,
|
||||
<WithComponentFirstStyledANew color={'black'} />,
|
||||
<WithComponentFirstStyledA color={"black"} />,
|
||||
<WithComponentFirstStyledB b={2} color={"black"} />,
|
||||
<WithComponentFirstStyledANew color={"black"} />
|
||||
];
|
||||
|
||||
const WithComponentRequired = styled((props: { to: string }) => <a href={props.to}/>)``;
|
||||
const WithComponentRequired = styled((props: { to: string }) => (
|
||||
<a href={props.to} />
|
||||
))``;
|
||||
// These tests pass in tsservice, but they fail in dtslint. I do not know why.
|
||||
// <WithComponentRequired href=''/>; // $ExpectError
|
||||
// <WithComponentRequired to=''/>;
|
||||
|
||||
const WithComponentRequired2 = WithComponentRequired.withComponent('a');
|
||||
const WithComponentRequired2 = WithComponentRequired.withComponent("a");
|
||||
// These tests pass in tsservice, but they fail in dtslint. I do not know why.
|
||||
// <WithComponentRequired2 href=''/>;
|
||||
// <WithComponentRequired2 to=''/>; // $ExpectError
|
||||
@@ -569,7 +576,7 @@ const asTest = (
|
||||
);
|
||||
|
||||
interface TestContainerProps {
|
||||
size: 'big' | 'small';
|
||||
size: "big" | "small";
|
||||
test?: boolean;
|
||||
}
|
||||
const TestContainer = ({ size, test }: TestContainerProps) => {
|
||||
@@ -581,7 +588,7 @@ const StyledTestContainer = styled(TestContainer)`
|
||||
`;
|
||||
|
||||
interface Test2ContainerProps {
|
||||
type: 'foo' | 'bar';
|
||||
type: "foo" | "bar";
|
||||
}
|
||||
class Test2Container extends React.Component<Test2ContainerProps> {
|
||||
render() {
|
||||
@@ -592,28 +599,175 @@ class Test2Container extends React.Component<Test2ContainerProps> {
|
||||
const containerTest = (
|
||||
// TODO (TypeScript 3.2): once the polymorphic overload is un-commented-out this should be the correct test
|
||||
// <StyledTestContainer as={Test2Container} type='foo' />
|
||||
<StyledTestContainer as={Test2Container} size='small' />
|
||||
<StyledTestContainer as={Test2Container} size="small" />
|
||||
);
|
||||
|
||||
// 4.0 refs
|
||||
|
||||
const divFnRef = (ref: HTMLDivElement|null) => { /* empty */ };
|
||||
const divFnRef = (ref: HTMLDivElement | null) => {
|
||||
/* empty */
|
||||
};
|
||||
const divRef = React.createRef<HTMLDivElement>();
|
||||
|
||||
const StyledDiv = styled.div``;
|
||||
|
||||
<StyledDiv ref={divRef}/>;
|
||||
<StyledDiv ref={divFnRef}/>;
|
||||
<StyledDiv ref='string'/>; // $ExpectError
|
||||
<StyledDiv ref={divRef} />;
|
||||
<StyledDiv ref={divFnRef} />;
|
||||
<StyledDiv ref="string" />; // $ExpectError
|
||||
|
||||
const StyledStyledDiv = styled(StyledDiv)``;
|
||||
<StyledStyledDiv ref={divRef}/>;
|
||||
<StyledStyledDiv ref={divFnRef}/>;
|
||||
<StyledStyledDiv ref='string'/>; // $ExpectError
|
||||
<StyledStyledDiv ref={divRef} />;
|
||||
<StyledStyledDiv ref={divFnRef} />;
|
||||
<StyledStyledDiv ref="string" />; // $ExpectError
|
||||
|
||||
const StyledA = StyledDiv.withComponent('a');
|
||||
<StyledA ref={divRef}/>; // $ExpectError
|
||||
<StyledA ref={ref => {
|
||||
// $ExpectType HTMLAnchorElement | null
|
||||
ref;
|
||||
}}/>;
|
||||
const StyledA = StyledDiv.withComponent("a");
|
||||
<StyledA ref={divRef} />; // $ExpectError
|
||||
<StyledA
|
||||
ref={ref => {
|
||||
// $ExpectType HTMLAnchorElement | null
|
||||
ref;
|
||||
}}
|
||||
/>;
|
||||
|
||||
async function typedThemes() {
|
||||
const theme = {
|
||||
color: "green"
|
||||
};
|
||||
|
||||
// abuse "await import(...)" to be able to reference the styled-components namespace
|
||||
// without actually doing a top level namespace import
|
||||
const {
|
||||
default: styled,
|
||||
css,
|
||||
createGlobalStyle,
|
||||
ThemeProvider,
|
||||
ThemeConsumer
|
||||
} = (await import("styled-components")) as ThemedStyledComponentsModule<
|
||||
typeof theme
|
||||
>;
|
||||
|
||||
const ThemedDiv = styled.div`
|
||||
background: ${props => {
|
||||
// $ExpectType string
|
||||
props.theme.color;
|
||||
// $ExpectType number | undefined
|
||||
props.tabIndex;
|
||||
return props.theme.color;
|
||||
}};
|
||||
`;
|
||||
const ThemedDiv2 = styled.div(props => {
|
||||
// $ExpectType string
|
||||
props.theme.color;
|
||||
// $ExpectType number | undefined
|
||||
props.tabIndex;
|
||||
|
||||
return {
|
||||
background: props.theme.color
|
||||
};
|
||||
});
|
||||
const ThemedDiv3 = styled.div(props => {
|
||||
// $ExpectType string
|
||||
props.theme.color;
|
||||
// $ExpectType number | undefined
|
||||
props.tabIndex;
|
||||
|
||||
return css`
|
||||
background: ${props.theme.color};
|
||||
`;
|
||||
});
|
||||
const themedCss = css`
|
||||
background: ${props => {
|
||||
// $ExpectType string
|
||||
props.theme.color;
|
||||
// $ExpectType "theme"
|
||||
type Keys = keyof typeof props;
|
||||
return props.theme.color;
|
||||
}};
|
||||
`;
|
||||
const ThemedDiv4 = styled.div(themedCss);
|
||||
|
||||
const themedCssWithNesting = css(props => ({
|
||||
color: props.theme.color,
|
||||
[ThemedDiv3]: {
|
||||
color: "green"
|
||||
}
|
||||
}));
|
||||
|
||||
const Global = createGlobalStyle`
|
||||
${themedCssWithNesting}
|
||||
`;
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<>
|
||||
<Global />
|
||||
<ThemedDiv />
|
||||
<ThemedDiv2 />
|
||||
<ThemedDiv3 />
|
||||
<ThemedDiv4 />
|
||||
<ThemeConsumer>
|
||||
{theme => {
|
||||
// $ExpectType string
|
||||
theme.color;
|
||||
return theme.color;
|
||||
}}
|
||||
</ThemeConsumer>
|
||||
</>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
async function reexportCompatibility() {
|
||||
const sc = await import("styled-components");
|
||||
const themed = sc as ThemedStyledComponentsModule<any>;
|
||||
|
||||
let { ...scExports } = sc;
|
||||
let { ...themedExports } = themed;
|
||||
// both branches must be assignable to each other
|
||||
if (Math.random()) {
|
||||
scExports = themedExports;
|
||||
} else {
|
||||
themedExports = scExports;
|
||||
}
|
||||
}
|
||||
|
||||
async function themeAugmentation() {
|
||||
interface BaseTheme {
|
||||
background: string;
|
||||
}
|
||||
interface ExtraTheme extends BaseTheme {
|
||||
accent: string;
|
||||
}
|
||||
|
||||
const base = (await import("styled-components")) as ThemedStyledComponentsModule<
|
||||
BaseTheme
|
||||
>;
|
||||
const extra = (await import("styled-components")) as ThemedStyledComponentsModule<
|
||||
ExtraTheme,
|
||||
BaseTheme
|
||||
>;
|
||||
|
||||
return (
|
||||
<base.ThemeProvider
|
||||
theme={{
|
||||
background: "black"
|
||||
}}
|
||||
>
|
||||
<>
|
||||
<extra.ThemeProvider
|
||||
theme={base => base} // $ExpectError
|
||||
>
|
||||
<extra.ThemeConsumer>{() => null}</extra.ThemeConsumer>
|
||||
</extra.ThemeProvider>
|
||||
<extra.ThemeProvider
|
||||
theme={base => ({
|
||||
...base,
|
||||
accent: "blue"
|
||||
})}
|
||||
>
|
||||
<extra.ThemeConsumer>{() => null}</extra.ThemeConsumer>
|
||||
</extra.ThemeProvider>
|
||||
</>
|
||||
</base.ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user