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

Migrate Counter component to TS #322

Merged
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 55 additions & 36 deletions components/counter/index.js → components/counter/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { forwardRef } from '@wordpress/element';
import PropTypes from 'prop-types';
import cx from 'classnames';
import styled from '@emotion/styled';
import { StyledComponentContext } from '../styled-components-context';
import { ForwardRefExoticComponent, PropsWithoutRef, RefAttributes, FC } from 'react';

const StyledSvg = styled('svg')`
transform: rotate(-90deg);
Expand Down Expand Up @@ -56,8 +56,16 @@ const StyledCounter = styled('div')`
font-variant-numeric: tabular-nums;
`;

const CircularProgressBar = (props) => {
const { percentage } = props;
interface CircularProgressBarProps {
/**
* Percentage elapsed.
*/
percentage: number;
}

const CircularProgressBar: FC<CircularProgressBarProps> = ({
percentage
}) => {
const radius = 90;
const circumference = 2 * Math.PI * radius;

Expand Down Expand Up @@ -139,38 +147,49 @@ const CircularProgressBar = (props) => {
);
};

/**
* Counter
*
* @description display character count and limit.
*
* @returns <Counter />
*/
const Counter = forwardRef((props, ref) => {
const { count, limit } = props;
const percentage = (count / limit) * 100;
return (
<StyledComponentContext cacheKey="tenup-component-counter">
<StyledCounter
className={cx('tenup--block-components__character-count', {
'is-over-limit': count > limit,
})}
{...props}
ref={ref}
>
<div className="tenup--block-components__character-count__label">
<span className="tenup--block-components__character-count__count">{count}</span>{' '}
/{' '}
<span className="tenup--block-components__character-count__limit">{limit}</span>
</div>
<CircularProgressBar percentage={percentage} />
</StyledCounter>
</StyledComponentContext>
);
});

CircularProgressBar.propTypes = {
percentage: PropTypes.number.isRequired,
};
interface CounterProps {
/**
* Current count.
*/
count: number;

/**
* Max limit.
*/
limit: number;

/**
* Rest of the props.
*/
[key: string]: unknown;
fabiankaegy marked this conversation as resolved.
Show resolved Hide resolved
}

const Counter: ForwardRefExoticComponent<PropsWithoutRef<CounterProps> & RefAttributes<HTMLDivElement>> = forwardRef<HTMLDivElement, CounterProps>(
({
count,
limit,
...rest
}, ref ) => {
const percentage = (count / limit) * 100;
return (
<StyledComponentContext cacheKey="tenup-component-counter">
<StyledCounter
className={cx('tenup--block-components__character-count', {
'is-over-limit': count > limit,
})}
ref={ref}
{...rest}
>
<div className="tenup--block-components__character-count__label">
<span className="tenup--block-components__character-count__count">{count}</span>{' '}
/{' '}
<span className="tenup--block-components__character-count__limit">{limit}</span>
</div>
<CircularProgressBar percentage={percentage} />
</StyledCounter>
</StyledComponentContext>
);
}
);

export { CircularProgressBar, Counter };
42 changes: 42 additions & 0 deletions components/counter/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Counter

The `Counter` component let's you use circular progress bar with two props - `count` and `limit`.

This also exposes another component named `CircularProgressBar`, which can be used when we don't want the text annotation and just the SVG illustration.

## Usage

```js
import { Counter, CircularProgressBar } from '@10up/block-components';

function MyComponent( props ) {

return (
<>
<Counter
count={text.length}
limit={20}
/>

<CircularProgressBar
percentage={(text.length / 20) * 100}
/>
</>
);
}
```

## Props

#### Counter

| Name | Type | Default | isRequired | Description |
| ---------------- | ---------- | ---------- | --------------------- | ---------------------------------------------------------------------- |
| `count` | `number` | - | `Yes` | Current count of the counter. |
| `limit` | `number` | - | `Yes` | Max limit of the counter. |

#### CircularProgressBar

| Name | Type | Default | isRequired | Description |
| ---------------- | ---------- | ---------- | --------------------- | ---------------------------------------------------------------------- |
| `percentage` | `number` | - | `Yes` | Elapsed percentage of the timer. |
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@ import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';
import { useRefEffect, useInstanceId } from '@wordpress/compose';
import { useState } from '@wordpress/element';
import propTypes from 'prop-types';

export const StyledComponentContext = (props) => {
const { children, cacheKey } = props;
const fallbackKey = useInstanceId(StyledComponentContext);
interface StyledComponentContextProps {
/**
* Children.
*/
children: React.ReactNode;

/**
* Cache key.
*/
cacheKey: string;
}

export const StyledComponentContext: React.FC<StyledComponentContextProps> = ({
children,
cacheKey,
}) => {
const fallbackKey = `${useInstanceId(StyledComponentContext)}`;

const defaultCache = createCache({
key: cacheKey || fallbackKey,
Expand Down Expand Up @@ -37,8 +50,3 @@ export const StyledComponentContext = (props) => {
</>
);
};

StyledComponentContext.propTypes = {
children: propTypes.node.isRequired,
cacheKey: propTypes.string.isRequired,
};
14 changes: 14 additions & 0 deletions example/src/blocks/counter-example/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"apiVersion": 2,
"name": "example/counter",
"title": "Counter Example",
"description": "Example Block to show the Counter in usage",
"icon": "smiley",
"category": "common",
"example": {},
"supports": {
"html": false
},
"variations": [],
"editorScript": "file:./index.js"
}
59 changes: 59 additions & 0 deletions example/src/blocks/counter-example/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { __ } from '@wordpress/i18n';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import { PanelBody, Placeholder, TextControl } from '@wordpress/components';

import { Counter } from '@10up/block-components';
import { useState } from '@wordpress/element';

export const BlockEdit = () => {
const blockProps = useBlockProps();

const [ text, setText ] = useState( '' );

return (
<>
<InspectorControls>
<PanelBody title={__('Counter', 'example')}>
<TextControl
label="Text"
help="Enter some text"
value={ text }
onChange={
( value ) => {
if ( value.length > 20 ) {
return;
}
setText( value );
}
}
/>
<Counter
count={text.length}
limit={20}
/>
</PanelBody>
</InspectorControls>
<div {...blockProps}>
<Placeholder label={__('Counter', 'example')} instructions={__('Counter component example', 'example')}>
<TextControl
label="Text"
help="Enter some text"
value={ text }
onChange={
( value ) => {
if ( value.length > 20 ) {
return;
}
setText( value );
}
}
/>
<Counter
count={text.length}
limit={20}
/>
</Placeholder>
</div>
</>
)
}
8 changes: 8 additions & 0 deletions example/src/blocks/counter-example/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { registerBlockType } from '@wordpress/blocks';
import metadata from './block.json';
import { BlockEdit } from './edit';

registerBlockType( metadata, {
edit: BlockEdit,
save: () => null
} );
Loading