Skip to content

Commit

Permalink
Adjust flow types to handle function calls (#1188)
Browse files Browse the repository at this point in the history
* Adjust flow types to handle function calls

* Add optional props to classnames

* Add changeset

* Update packages/react/src/styled/__tests__/types.test.js.flow

Co-authored-by: Monica <molejniczak@atlassian.com>

* Split classnames function decl for CSS

* Test conditional logical expressions in styled

Co-authored-by: Monica <molejniczak@atlassian.com>
  • Loading branch information
JakeLane and MonicaOlejniczak committed Apr 14, 2022
1 parent c4ce8a4 commit ad4d257
Show file tree
Hide file tree
Showing 37 changed files with 383 additions and 268 deletions.
8 changes: 8 additions & 0 deletions .changeset/giant-olives-deny.md
@@ -0,0 +1,8 @@
---
'@compiled/css': patch
'@compiled/jest': patch
'@compiled/react': patch
'@compiled/webpack-loader': patch
---

Update TypeScript and Flow types to support function calls and resolve incorrect typing
13 changes: 13 additions & 0 deletions flow-typed/tokens.js
@@ -0,0 +1,13 @@
declare var layers: {
modal: () => number,
};

declare type Tokens = typeof tokens;
declare type CSSTokenMap = {
'elevation.surface': 'var(--ds-surface)',
};
declare var tokens: {
+'elevation.surface': '--ds-surface',
};
declare type CSSToken = $ElementType<CSSTokenMap, $Keys<CSSTokenMap>>;
declare function token<T: $Keys<Tokens>>(path: T, fallback?: string): $ElementType<CSSTokenMap, T>;
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -97,8 +97,8 @@
"eslint-plugin-json-files": "^1.3.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.4.0",
"flow-bin": "^0.172.0",
"flowgen": "^1.15.0",
"flow-bin": "^0.174.1",
"flowgen": "^1.17.0",
"git-branch-is": "^4.0.0",
"husky": "^4.3.8",
"jest": "^26.6.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/jest/src/index.js.flow
@@ -1,7 +1,7 @@
/**
* Flowtype definitions for index
* Generated by Flowgen from a Typescript Definition
* Flowgen v1.15.0
* Flowgen v1.17.0
* @flow
*/
import type { MatchFilter } from './types';
Expand Down
2 changes: 1 addition & 1 deletion packages/jest/src/matchers.js.flow
@@ -1,7 +1,7 @@
/**
* Flowtype definitions for matchers
* Generated by Flowgen from a Typescript Definition
* Flowgen v1.15.0
* Flowgen v1.17.0
* @flow
*/
import type { MatchFilter } from './types';
Expand Down
2 changes: 1 addition & 1 deletion packages/jest/src/types.js.flow
@@ -1,7 +1,7 @@
/**
* Flowtype definitions for types
* Generated by Flowgen from a Typescript Definition
* Flowgen v1.15.0
* Flowgen v1.17.0
* @flow
*/
import type { Pseudos } from 'csstype';
Expand Down
13 changes: 10 additions & 3 deletions packages/react/src/class-names/__tests__/types.test.js.flow
@@ -1,6 +1,6 @@
// @flow strict-local
import React, { type Node } from 'react';
import { ClassNames, type CssFunction } from '@compiled/react';
import { ClassNames, type CssType } from '@compiled/react';

// Object call expression
({ children }: { children: Node }): React$Element<typeof ClassNames> => (
Expand All @@ -23,6 +23,13 @@ import { ClassNames, type CssFunction } from '@compiled/react';

// Array
({ children }: { children: Node }): React$Element<typeof ClassNames> => {
const classes: CssFunction<>[] = [{ fontSize: 12 }, `font-size: 12px`];
return <ClassNames>{({ css }) => <span className={css(classes)}>{children}</span>}</ClassNames>;
return (
<ClassNames>
{({ css }) => (
<span className={css(([{ fontSize: 12 }, `font-size: 12px`]: CssType<void>[]))}>
{children}
</span>
)}
</ClassNames>
);
};
49 changes: 29 additions & 20 deletions packages/react/src/class-names/index.js.flow
@@ -1,45 +1,54 @@
/**
* Flowtype definitions for index
* Generated by Flowgen from a Typescript Definition
* Flowgen v1.15.0
* Flowgen v1.17.0
* @flow
*/
import type { Node } from 'react';
import type { BasicTemplateInterpolations, CssFunction } from '../types';
export type Interpolations = (BasicTemplateInterpolations | CssFunction<> | CssFunction<>[])[];
export interface ClassNamesProps {
import type { CssType, CssFunction } from '../types';
export type ObjectInterpolation<TProps> = CssType<TProps> | CssType<TProps>[];
export type TemplateStringsInterpolation<TProps> = CssFunction<TProps> | CssFunction<TProps>[];
declare interface CssSignature<TProps> {
(...interpolations: ObjectInterpolation<TProps>[]): string;
(
template: $ReadOnlyArray<string>,
...interpolations: TemplateStringsInterpolation<TProps>[]
): string;
}
export interface ClassNamesProps<TProps> {
children: (opts: {
css: (css: CssFunction<> | CssFunction<>[], ...interpolations: Interpolations) => string,
style: {
[key: string]: string,
...
},
css: CssSignature<TProps>,
style: $Shape<CSSStyleDeclaration>,
...
}) => Node;
}
/**
* Use a component where styles are not necessarily tied to an element.
* ## Class names
*
* ```
* // Object CSS
* Use a component where styles are not necessarily used on a JSX element.
* For further details [read the documentation](https://compiledcssinjs.com/docs/api-class-names).
*
* ### Style with objects
* @example ```
* <ClassNames>
* {({ css, style }) => children({ className: css({ fontSize: 12 }) })}
* </ClassNames>
* ```
*
* // Template literal CSS
* ### Style with template literals
* @example ```
* <ClassNames>
* {({ css, style }) => children({ className: css`font-size: 12px;` })}
* </ClassNames>
* ```
*
* // Array CSS
* ### Compose styles with arrays
* @example ```
* <ClassNames>
* {({ css, style }) =>
* children({ className: css([{ fontSize: 12 }, `font-size: 12px`]) })}
* children({ className: css([{ fontSize: 12 }, css`font-size: 12px`]) })}
* </ClassNames>
* ```
*
* For more help, read the docs:
* https://compiledcssinjs.com/docs/api-class-names
* @param props
*/
declare export function ClassNames(_: ClassNamesProps): React$Node;
declare export function ClassNames<TProps>(x: ClassNamesProps<TProps>): React$Node;
declare export {};
24 changes: 15 additions & 9 deletions packages/react/src/class-names/index.ts
@@ -1,15 +1,21 @@
import type { ReactNode, CSSProperties } from 'react';

import type { BasicTemplateInterpolations, CssFunction } from '../types';
import type { CssType, CssFunction } from '../types';
import { createSetupError } from '../utils/error';

export type Interpolations = (BasicTemplateInterpolations | CssFunction | CssFunction[])[];
export type ObjectInterpolation<TProps> = CssType<TProps> | CssType<TProps>[];
export type TemplateStringsInterpolation<TProps> = CssFunction<TProps> | CssFunction<TProps>[];

export interface ClassNamesProps {
children: (opts: {
css: (css: CssFunction | CssFunction[], ...interpolations: Interpolations) => string;
style: CSSProperties;
}) => ReactNode;
interface CssSignature<TProps> {
(...interpolations: ObjectInterpolation<TProps>[]): string;
(
template: TemplateStringsArray,
...interpolations: TemplateStringsInterpolation<TProps>[]
): string;
}

export interface ClassNamesProps<TProps> {
children: (opts: { css: CssSignature<TProps>; style: CSSProperties }) => ReactNode;
}

/**
Expand Down Expand Up @@ -46,8 +52,8 @@ export interface ClassNamesProps {
* </ClassNames>
* ```
*/
export function ClassNames({ children }: ClassNamesProps): JSX.Element;
export function ClassNames<TProps = void>({ children }: ClassNamesProps<TProps>): JSX.Element;

export function ClassNames(_props: ClassNamesProps): JSX.Element {
export function ClassNames<TProps = void>(_props: ClassNamesProps<TProps>): JSX.Element {
throw createSetupError();
}
25 changes: 25 additions & 0 deletions packages/react/src/css/__tests__/types.test.js.flow
Expand Up @@ -15,3 +15,28 @@ import { css } from '@compiled/react';
const styles = css({ color: 'red' });
return <div css={styles}>red text</div>;
};

// Object call with function call
(): React$Element<'div'> => {
const styles = css({
backgroundColor: token('elevation.surface', 'white'),
zIndex: layers.modal(),
});
return <div css={styles}>red text</div>;
};

// Media query selector
css({
/* Style only for Google Chrome */
'@media screen and (-webkit-min-device-pixel-ratio: 0)': {
wordBreak: 'break-word',
},
/* Style only for Safari */
'@media screen and (-webkit-transition)': {
wordBreak: 'break-word',
},
/* Style only for Mozilla Firefox */
'@-moz-document url-prefix()': {
overflowWrap: 'anywhere',
},
});
47 changes: 24 additions & 23 deletions packages/react/src/css/index.js.flow
@@ -1,35 +1,36 @@
/**
* Flowtype definitions for index
* Generated by Flowgen from a Typescript Definition
* Flowgen v1.15.0
* Flowgen v1.17.0
* @flow
*/
import type { BasicTemplateInterpolations, CSSProps, FunctionInterpolation } from '../types';
import type { CSSProps, CssObject, CssFunction } from '../types';
/**
* Create styles that can be re-used between components with a template literal.
* ## CSS
*
* ```
* css`color: red;`;
* ```
* Define styles that are statically typed and useable with other Compiled APIs.
* For further details [read the documentation](https://compiledcssinjs.com/docs/api-css).
*
* For more help, read the docs:
* https://compiledcssinjs.com/docs/api-css
* @param css
* @param values
*/
declare export default function css<T>(
_css: $ReadOnlyArray<string>,
..._values: (BasicTemplateInterpolations | FunctionInterpolation<T>)[]
): CSSProps;
/**
* Create styles that can be re-used between components with an object
* ### Style with objects
* @example ```
* const redText = css({
* color: 'red',
* });
*
* <div css={redText} />
* ```
* css({ color: 'red' });
* ```
*
* For more help, read the docs:
* https://compiledcssinjs.com/docs/api-css
* @param css
* ### Style with template literals
* @example ```
* const redText = css`
* color: red;
* `;
*
* <div css={redText} />
* ```
*/
declare export default function css(_css: CSSProps): CSSProps;
declare export default function css<TProps>(
styles: $ReadOnlyArray<string>,
...interpolations: CssFunction<TProps>[]
): CSSProps<TProps>;
declare export default function css<T>(styles: CssObject<T> | CssObject<T>[]): CSSProps<T>;
21 changes: 8 additions & 13 deletions packages/react/src/css/index.ts
@@ -1,11 +1,6 @@
/* eslint-disable import/export */

import type {
AnyKeyCssProps,
BasicTemplateInterpolations,
CSSProps,
FunctionInterpolation,
} from '../types';
import type { CSSProps, CssObject, CssFunction } from '../types';
import { createSetupError } from '../utils/error';

/**
Expand Down Expand Up @@ -36,16 +31,16 @@ import { createSetupError } from '../utils/error';
* <div css={redText} />
* ```
*/
export default function css<T = void>(
export default function css<TProps = void>(
styles: TemplateStringsArray,
...interpolations: (BasicTemplateInterpolations | FunctionInterpolation<T>)[]
): CSSProps;
...interpolations: CssFunction<TProps>[]
): CSSProps<TProps>;

export default function css(styles: AnyKeyCssProps<void> | CSSProps): CSSProps;
export default function css<T = void>(styles: CssObject<T> | CssObject<T>[]): CSSProps<T>;

export default function css<T = void>(
_styles: TemplateStringsArray | CSSProps,
..._interpolations: (BasicTemplateInterpolations | FunctionInterpolation<T>)[]
): CSSProps {
_styles: TemplateStringsArray | CssObject<T> | CssObject<T>[],
..._interpolations: CssFunction[]
): CSSProps<T> {
throw createSetupError();
}
11 changes: 4 additions & 7 deletions packages/react/src/index.js.flow
@@ -1,15 +1,12 @@
/**
* THIS IS A MANUALLY CURATED FLOW FILE.
*
* Flowtype definitions for runtime
* Flowtype definitions for index
* Generated by Flowgen from a Typescript Definition
* Flowgen v1.15.0
* Flowgen v1.17.0
* @flow
*/
import type { CSSProps, CssFunction } from './types';
import type { CssFunction, CSSProps, CssType } from './types';
export type { CSSProps, CssFunction, CssType };
declare export { keyframes } from './keyframes';
declare export { styled } from './styled';
declare export { ClassNames } from './class-names';
declare export { default as css } from './css';
export type { CssFunction, CSSProps };
export type { CssObject } from './styled';
7 changes: 3 additions & 4 deletions packages/react/src/index.ts
@@ -1,7 +1,9 @@
import { createElement } from 'react';

import type { CompiledJSX } from './jsx/jsx-local-namespace';
import type { CssFunction, CSSProps } from './types';
import type { CssFunction, CSSProps, CssType } from './types';

export type { CSSProps, CssFunction, CssType };

export { keyframes } from './keyframes';
export { styled } from './styled';
Expand All @@ -12,9 +14,6 @@ export { default as css } from './css';
// Compiled currently doesn't define its own and uses this purely to enable a local jsx namespace.
export const jsx = createElement;

export type { CssFunction, CSSProps };
export type { CssObject } from './styled';

export namespace jsx {
export namespace JSX {
export type Element = CompiledJSX.Element;
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/jsx/jsx-local-namespace.ts
Expand Up @@ -41,7 +41,7 @@ type WithConditionalCSSProp<TProps> = 'className' extends keyof TProps
* />
* ```
*/
css?: CssFunction | CssFunction[];
css?: CssFunction<void> | CssFunction<void>[];
}
: // eslint-disable-next-line @typescript-eslint/ban-types
{}
Expand Down Expand Up @@ -69,7 +69,7 @@ export namespace CompiledJSX {
export type IntrinsicClassAttributes<T> = ReactJSXIntrinsicClassAttributes<T>;
export type IntrinsicElements = {
[K in keyof ReactJSXIntrinsicElements]: ReactJSXIntrinsicElements[K] & {
css?: CssFunction | CssFunction[];
css?: CssFunction<void> | CssFunction<void>[];
};
};
}

0 comments on commit ad4d257

Please sign in to comment.