Skip to content

Commit

Permalink
Card: migrate to TypeScript (#42941)
Browse files Browse the repository at this point in the history
* rename to ts

* Replace component with tsx.

* move MediaProps to types.ts

* fix imports

* component props to DRY

* convert to ts.

* fix type.

* remove generics

* fix types

* named export

* remove args

* fix CONTRIBUTING.md

* fix import order

* Update packages/components/src/card/card/hook.ts

Co-authored-by: Marco Ciampini <marco.ciampo@gmail.com>

* convert to typescript

* remove `@example` .

* add wrapper

* fix path

* rename snapshot

* add CHANGELOG.md

Co-authored-by: Marco Ciampini <marco.ciampo@gmail.com>
  • Loading branch information
torounit and ciampo committed Aug 23, 2022
1 parent 6b393f8 commit c692b4e
Show file tree
Hide file tree
Showing 28 changed files with 215 additions and 313 deletions.
2 changes: 2 additions & 0 deletions packages/components/CHANGELOG.md
Expand Up @@ -47,13 +47,15 @@
- `Modal`: use `KeyboardEvent.code` instead of deprecated `KeyboardEvent.keyCode`. improve unit tests ([#43429](https://github.com/WordPress/gutenberg/pull/43429/)).
- `FocalPointPicker`: use `KeyboardEvent.code`, partially refactor tests to modern RTL and `user-event` ([#43441](https://github.com/WordPress/gutenberg/pull/43441/)).
- `CustomGradientPicker`: use `KeyboardEvent.code` instead of `KeyboardEvent.keyCode` ([#43437](https://github.com/WordPress/gutenberg/pull/43437/)).
- `Card`: Convert to TypeScript ([#42941](https://github.com/WordPress/gutenberg/pull/42941)).
- `NavigableContainer`: Refactor away from `_.omit()` ([#43474](https://github.com/WordPress/gutenberg/pull/43474/)).
- `Notice`: Refactor away from `_.omit()` ([#43474](https://github.com/WordPress/gutenberg/pull/43474/)).
- `Snackbar`: Refactor away from `_.omit()` ([#43474](https://github.com/WordPress/gutenberg/pull/43474/)).
- `UnitControl`: Refactor away from `_.omit()` ([#43474](https://github.com/WordPress/gutenberg/pull/43474/)).
- `BottomSheet`: Refactor away from `_.omit()` ([#43474](https://github.com/WordPress/gutenberg/pull/43474/)).

### Experimental

- `FormTokenField`: add `__experimentalAutoSelectFirstMatch` prop to auto select the first matching suggestion on typing ([#42527](https://github.com/WordPress/gutenberg/pull/42527/)).

## 19.17.0 (2022-08-10)
Expand Down
20 changes: 10 additions & 10 deletions packages/components/CONTRIBUTING.md
Expand Up @@ -156,8 +156,8 @@ function Example(

A couple of good examples of how hooks are used for composition are:

- the `Card` component, which builds on top of the `Surface` component by [calling the `useSurface` hook inside its own hook](/packages/components/src/card/card/hook.js);
- the `HStack` component, which builds on top of the `Flex` component and [calls the `useFlex` hook inside its own hook](/packages/components/src/h-stack/hook.js).
- the `Card` component, which builds on top of the `Surface` component by [calling the `useSurface` hook inside its own hook](/packages/components/src/card/card/hook.ts);
- the `HStack` component, which builds on top of the `Flex` component and [calls the `useFlex` hook inside its own hook](/packages/components/src/h-stack/hook.tsx).

<!-- ## API Consinstency
Expand Down Expand Up @@ -262,7 +262,7 @@ An example of how this is used can be found in the [`Card` component family](/pa

```jsx
//=========================================================================
// Simplified snippet from `packages/components/src/card/card/hook.js`
// Simplified snippet from `packages/components/src/card/card/hook.ts`
//=========================================================================
import { useContextSystem } from '../../ui/context';

Expand All @@ -276,7 +276,7 @@ export function useCard( props ) {
}

//=========================================================================
// Simplified snippet from `packages/components/src/card/card/component.js`
// Simplified snippet from `packages/components/src/card/card/component.ts`
//=========================================================================
import { contextConnect, ContextSystemProvider } from '../../ui/context';

Expand All @@ -301,7 +301,7 @@ function Card( props, forwardedRef ) {
}, [ isBorderless, size ] );

return (
{ /* Write additional values to the Context System */ }
/* Write additional values to the Context System */
<ContextSystemProvider value={ contextProviderValue }>
{ /* [...] */ }
</ContextSystemProvider>
Expand All @@ -313,7 +313,7 @@ const ConnectedCard = contextConnect( Card, 'Card' );
export default ConnectedCard;

//=========================================================================
// Simplified snippet from `packages/components/src/card/card-body/hook.js`
// Simplified snippet from `packages/components/src/card/card-body/hook.ts`
//=========================================================================
import { useContextSystem } from '../../ui/context';

Expand Down Expand Up @@ -364,7 +364,7 @@ Primary.args = {

A great tool to use when writing stories is the [Storybook Controls addon](https://storybook.js.org/addons/@storybook/addon-controls). Ideally props should be exposed by using this addon, which provides a graphical UI to interact dynamically with the component without needing to write code. Avoid using [Knobs](https://storybook.js.org/addons/@storybook/addon-knobs) for new stories, as this addon is deprecated.

The default value of each control should coincide with the default value of the props (i.e. it should be `undefined` if a prop is not required). A story should, therefore, also explicitly show how values from the Context System are applied to (sub)components. A good example of how this may look like is the [`Card` story](https://wordpress.github.io/gutenberg/?path=/story/components-card--default) (code [here](/packages/components/src/card/stories/index.js)).
The default value of each control should coincide with the default value of the props (i.e. it should be `undefined` if a prop is not required). A story should, therefore, also explicitly show how values from the Context System are applied to (sub)components. A good example of how this may look like is the [`Card` story](https://wordpress.github.io/gutenberg/?path=/story/components-card--default) (code [here](/packages/components/src/card/stories/index.tsx)).

Storybook can be started on a local machine by running `npm run storybook:dev`. Alternatively, the components' catalogue (up to date with the latest code on `trunk`) can be found at [wordpress.github.io/gutenberg/](https://wordpress.github.io/gutenberg/).

Expand Down Expand Up @@ -516,7 +516,7 @@ Given a component folder (e.g. `packages/components/src/unit-control`):
5. Extend existing components’ props if possible, especially when a component internally forwards its props to another component in the package.
6. If the component forwards its `...restProps` to an underlying element/component, you should use the `WordPressComponentProps` type for the component's props:

```jsx
```tsx
import type { WordPressComponentProps } from '../ui/context';
import type { ComponentOwnProps } from './types';

Expand All @@ -533,7 +533,7 @@ Given a component folder (e.g. `packages/components/src/unit-control`):

7. If the component doesn't forwards its ref yet, wrap the component in a `forwardRed` call. Alternatively, if you want to take advantage of the [Context system](#context-system), you can use the `contextConnect` utility function (which also takes care of adding ref forwarding)

```jsx
```tsx
// With `forwardRef`
import type { ForwardedRef } from 'react';
import { forwardRef } from '@wordpress/element';
Expand All @@ -554,7 +554,7 @@ Given a component folder (e.g. `packages/components/src/unit-control`):
export default MyComponent;
```

```jsx
```tsx
// With `contextConnect`
import type { ForwardedRef } from 'react';
import {
Expand Down
@@ -1,16 +1,21 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';

/**
* Internal dependencies
*/
import { contextConnect } from '../../ui/context';
import { contextConnect, WordPressComponentProps } from '../../ui/context';
import { Scrollable } from '../../scrollable';
import { View } from '../../view';
import { useCardBody } from './hook';
import type { BodyProps } from '../types';

/**
* @param {import('../../ui/context').WordPressComponentProps<import('../types').BodyProps, 'div'>} props
* @param {import('react').ForwardedRef<any>} forwardedRef
*/
function CardBody( props, forwardedRef ) {
function UnconnectedCardBody(
props: WordPressComponentProps< BodyProps, 'div' >,
forwardedRef: ForwardedRef< any >
) {
const { isScrollable, ...otherProps } = useCardBody( props );

if ( isScrollable ) {
Expand All @@ -24,7 +29,6 @@ function CardBody( props, forwardedRef ) {
* `CardBody` renders an optional content area for a `Card`.
* Multiple `CardBody` components can be used within `Card` if needed.
*
* @example
* ```jsx
* import { Card, CardBody } from `@wordpress/components`;
*
Expand All @@ -35,6 +39,6 @@ function CardBody( props, forwardedRef ) {
* </Card>
* ```
*/
const ConnectedCardBody = contextConnect( CardBody, 'CardBody' );
export const CardBody = contextConnect( UnconnectedCardBody, 'CardBody' );

export default ConnectedCardBody;
export default CardBody;
Expand Up @@ -6,14 +6,14 @@ import { useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
import { useContextSystem } from '../../ui/context';
import { useContextSystem, WordPressComponentProps } from '../../ui/context';
import * as styles from '../styles';
import { useCx } from '../../utils/hooks/use-cx';
import type { BodyProps } from '../types';

/**
* @param {import('../../ui/context').WordPressComponentProps<import('../types').BodyProps, 'div'>} props
*/
export function useCardBody( props ) {
export function useCardBody(
props: WordPressComponentProps< BodyProps, 'div' >
) {
const {
className,
isScrollable = false,
Expand Down
@@ -1,15 +1,19 @@
/**
* Internal dependencies
* External dependencies
*/
import { contextConnect } from '../../ui/context';
import { Divider } from '../../divider';
import { useCardDivider } from './hook';
import type { ForwardedRef } from 'react';

/**
* @param {import('../../ui/context').WordPressComponentProps<import('../../divider').DividerProps, 'hr', false>} props
* @param {import('react').ForwardedRef<any>} forwardedRef
* Internal dependencies
*/
function CardDivider( props, forwardedRef ) {
import { contextConnect, WordPressComponentProps } from '../../ui/context';
import { Divider, DividerProps } from '../../divider';
import { useCardDivider } from './hook';

function UnconnectedCardDivider(
props: WordPressComponentProps< DividerProps, 'hr', false >,
forwardedRef: ForwardedRef< any >
) {
const dividerProps = useCardDivider( props );

return <Divider { ...dividerProps } ref={ forwardedRef } />;
Expand All @@ -19,7 +23,6 @@ function CardDivider( props, forwardedRef ) {
* `CardDivider` renders an optional divider within a `Card`.
* It is typically used to divide multiple `CardBody` components from each other.
*
* @example
* ```jsx
* import { Card, CardBody, CardDivider } from `@wordpress/components`;
*
Expand All @@ -30,6 +33,9 @@ function CardDivider( props, forwardedRef ) {
* </Card>
* ```
*/
const ConnectedCardDivider = contextConnect( CardDivider, 'CardDivider' );
export const CardDivider = contextConnect(
UnconnectedCardDivider,
'CardDivider'
);

export default ConnectedCardDivider;
export default CardDivider;
Expand Up @@ -6,14 +6,14 @@ import { useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
import { useContextSystem } from '../../ui/context';
import { useContextSystem, WordPressComponentProps } from '../../ui/context';
import * as styles from '../styles';
import { useCx } from '../../utils/hooks/use-cx';
import type { DividerProps } from '../../divider';

/**
* @param {import('../../ui/context').WordPressComponentProps<import('../../divider').DividerProps, 'hr', false>} props
*/
export function useCardDivider( props ) {
export function useCardDivider(
props: WordPressComponentProps< DividerProps, 'hr', false >
) {
const { className, ...otherProps } = useContextSystem(
props,
'CardDivider'
Expand Down
@@ -1,15 +1,20 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';

/**
* Internal dependencies
*/
import { contextConnect } from '../../ui/context';
import { contextConnect, WordPressComponentProps } from '../../ui/context';
import { Flex } from '../../flex';
import { useCardFooter } from './hook';
import type { FooterProps } from '../types';

/**
* @param {import('../../ui/context').WordPressComponentProps<import('../types').FooterProps, 'div'>} props
* @param {import('react').ForwardedRef<any>} forwardedRef
*/
function CardFooter( props, forwardedRef ) {
function UnconnectedCardFooter(
props: WordPressComponentProps< FooterProps, 'div' >,
forwardedRef: ForwardedRef< any >
) {
const footerProps = useCardFooter( props );

return <Flex { ...footerProps } ref={ forwardedRef } />;
Expand All @@ -18,7 +23,6 @@ function CardFooter( props, forwardedRef ) {
/**
* `CardFooter` renders an optional footer within a `Card`.
*
* @example
* ```jsx
* import { Card, CardBody, CardFooter } from `@wordpress/components`;
*
Expand All @@ -28,6 +32,6 @@ function CardFooter( props, forwardedRef ) {
* </Card>
* ```
*/
const ConnectedCardFooter = contextConnect( CardFooter, 'CardFooter' );
export const CardFooter = contextConnect( UnconnectedCardFooter, 'CardFooter' );

export default ConnectedCardFooter;
export default CardFooter;
Expand Up @@ -6,14 +6,14 @@ import { useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
import { useContextSystem } from '../../ui/context';
import { useContextSystem, WordPressComponentProps } from '../../ui/context';
import * as styles from '../styles';
import { useCx } from '../../utils/hooks/use-cx';
import type { FooterProps } from '../types';

/**
* @param {import('../../ui/context').WordPressComponentProps<import('../types').FooterProps, 'div'>} props
*/
export function useCardFooter( props ) {
export function useCardFooter(
props: WordPressComponentProps< FooterProps, 'div' >
) {
const {
className,
justify,
Expand Down
@@ -1,15 +1,20 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';

/**
* Internal dependencies
*/
import { contextConnect } from '../../ui/context';
import { contextConnect, WordPressComponentProps } from '../../ui/context';
import { Flex } from '../../flex';
import { useCardHeader } from './hook';
import type { HeaderProps } from '../types';

/**
* @param {import('../../ui/context').WordPressComponentProps<import('../types').HeaderProps, 'div'>} props
* @param {import('react').ForwardedRef<any>} forwardedRef
*/
function CardHeader( props, forwardedRef ) {
function UnconnectedCardHeader(
props: WordPressComponentProps< HeaderProps, 'div' >,
forwardedRef: ForwardedRef< any >
) {
const headerProps = useCardHeader( props );

return <Flex { ...headerProps } ref={ forwardedRef } />;
Expand All @@ -18,7 +23,6 @@ function CardHeader( props, forwardedRef ) {
/**
* `CardHeader` renders an optional header within a `Card`.
*
* @example
* ```jsx
* import { Card, CardBody, CardHeader } from `@wordpress/components`;
*
Expand All @@ -28,6 +32,6 @@ function CardHeader( props, forwardedRef ) {
* </Card>
* ```
*/
const ConnectedCardHeader = contextConnect( CardHeader, 'CardHeader' );
export const CardHeader = contextConnect( UnconnectedCardHeader, 'CardHeader' );

export default ConnectedCardHeader;
export default CardHeader;
Expand Up @@ -6,14 +6,14 @@ import { useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
import { useContextSystem } from '../../ui/context';
import { useContextSystem, WordPressComponentProps } from '../../ui/context';
import * as styles from '../styles';
import { useCx } from '../../utils/hooks/use-cx';
import type { HeaderProps } from '../types';

/**
* @param {import('../../ui/context').WordPressComponentProps<import('../types').HeaderProps, 'div'>} props
*/
export function useCardHeader( props ) {
export function useCardHeader(
props: WordPressComponentProps< HeaderProps, 'div' >
) {
const {
className,
isBorderless = false,
Expand Down
@@ -1,15 +1,20 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';

/**
* Internal dependencies
*/
import { contextConnect } from '../../ui/context';
import { contextConnect, WordPressComponentProps } from '../../ui/context';
import { View } from '../../view';
import { useCardMedia } from './hook';
import type { MediaProps } from '../types';

/**
* @param {import('../../ui/context').WordPressComponentProps<{ children: import('react').ReactNode }, 'div'>} props
* @param {import('react').ForwardedRef<any>} forwardedRef
*/
function CardMedia( props, forwardedRef ) {
function UnconnectedCardMedia(
props: WordPressComponentProps< MediaProps, 'div' >,
forwardedRef: ForwardedRef< any >
) {
const cardMediaProps = useCardMedia( props );

return <View { ...cardMediaProps } ref={ forwardedRef } />;
Expand All @@ -32,6 +37,6 @@ function CardMedia( props, forwardedRef ) {
* );
* ```
*/
const ConnectedCardMedia = contextConnect( CardMedia, 'CardMedia' );
export const CardMedia = contextConnect( UnconnectedCardMedia, 'CardMedia' );

export default ConnectedCardMedia;
export default CardMedia;

0 comments on commit c692b4e

Please sign in to comment.