Browse Source

Explicitly define available props for components

Add event handler props for components.
tags/0.3.0
TheoryOfNekomata 4 years ago
parent
commit
8ce5a80ba5
10 changed files with 95 additions and 41 deletions
  1. +1
    -1
      lib/components/Button/Button.test.tsx
  2. +29
    -10
      lib/components/Button/Button.tsx
  3. +21
    -2
      lib/components/Checkbox/Checkbox.tsx
  4. +1
    -6
      lib/components/Icon/Icon.tsx
  5. +22
    -6
      lib/components/RadioButton/RadioButton.tsx
  6. +1
    -6
      lib/components/Select/Select.tsx
  7. +7
    -0
      lib/components/Slider/Slider.tsx
  8. +1
    -1
      lib/components/TextInput/TextInput.test.tsx
  9. +1
    -6
      lib/components/TextInput/TextInput.tsx
  10. +11
    -3
      package.json

+ 1
- 1
lib/components/Button/Button.test.tsx View File

@@ -48,7 +48,7 @@ describe('on unknown kinds', () => {


it('should render null', () => { it('should render null', () => {
fc.assert( fc.assert(
fc.property(fc.string().filter(s => !['button', 'a'].includes(s)), (element) => {
fc.property(fc.string().filter((s) => !['button', 'a'].includes(s)), (element) => {
const wrapper = Enzyme.shallow(<Button element={element as ButtonElement} />) const wrapper = Enzyme.shallow(<Button element={element as ButtonElement} />)


expect(wrapper.isEmptyRender()).toBe(true) expect(wrapper.isEmptyRender()).toBe(true)


+ 29
- 10
lib/components/Button/Button.tsx View File

@@ -156,6 +156,18 @@ const propTypes = {
* Does the button display a border? * Does the button display a border?
*/ */
border: PropTypes.bool, border: PropTypes.bool,
/**
* Event handler triggered when the component is clicked.
*/
onClick: PropTypes.func,
/**
* Event handler triggered when the component receives focus.
*/
onFocus: PropTypes.func,
/**
* Event handler triggered when the component loses focus.
*/
onBlur: PropTypes.func,
} }


type Props = PropTypes.InferProps<typeof propTypes> type Props = PropTypes.InferProps<typeof propTypes>
@@ -173,7 +185,9 @@ const Button = React.forwardRef<HTMLAnchorElement | HTMLButtonElement | HTMLSpan
rel, rel,
type = 'button', type = 'button',
border = false, border = false,
...etcProps
onClick,
onFocus,
onBlur,
}, },
ref, ref,
) => { ) => {
@@ -184,12 +198,7 @@ const Button = React.forwardRef<HTMLAnchorElement | HTMLButtonElement | HTMLSpan
} }
const buttonContent = ( const buttonContent = (
<React.Fragment> <React.Fragment>
{
border
&& (
<Border />
)
}
{border && <Border />}
{stringify(children)} {stringify(children)}
</React.Fragment> </React.Fragment>
) )
@@ -197,26 +206,36 @@ const Button = React.forwardRef<HTMLAnchorElement | HTMLButtonElement | HTMLSpan
switch (element) { switch (element) {
case 'button': case 'button':
return ( return (
<Base {...etcProps} type={type!} ref={ref as React.Ref<HTMLButtonElement>} disabled={disabled!} style={commonButtonStyles}>
<Base
onClick={onClick as React.EventHandler<React.SyntheticEvent>}
type={type!}
ref={ref as React.Ref<HTMLButtonElement>}
disabled={disabled!}
style={commonButtonStyles}
onFocus={onFocus as React.FocusEventHandler}
onBlur={onBlur as React.FocusEventHandler}
>
{buttonContent} {buttonContent}
</Base> </Base>
) )
case 'a': case 'a':
if (disabled) { if (disabled) {
return ( return (
<DisabledLinkBase {...etcProps} ref={ref as React.Ref<HTMLSpanElement>} style={commonButtonStyles}>
<DisabledLinkBase ref={ref as React.Ref<HTMLSpanElement>} style={commonButtonStyles}>
{buttonContent} {buttonContent}
</DisabledLinkBase> </DisabledLinkBase>
) )
} }
return ( return (
<LinkBase <LinkBase
{...etcProps}
onClick={onClick as React.EventHandler<React.SyntheticEvent>}
href={href!} href={href!}
target={target!} target={target!}
rel={rel!} rel={rel!}
ref={ref as React.Ref<HTMLAnchorElement>} ref={ref as React.Ref<HTMLAnchorElement>}
style={commonButtonStyles} style={commonButtonStyles}
onFocus={onFocus as React.FocusEventHandler}
onBlur={onBlur as React.FocusEventHandler}
> >
{buttonContent} {buttonContent}
</LinkBase> </LinkBase>


+ 21
- 2
lib/components/Checkbox/Checkbox.tsx View File

@@ -123,6 +123,18 @@ const propTypes = {
* Name of the form field associated with this component. * Name of the form field associated with this component.
*/ */
name: PropTypes.string, name: PropTypes.string,
/**
* Event handler triggered when the component is toggled.
*/
onChange: PropTypes.func,
/**
* Event handler triggered when the component receives focus.
*/
onFocus: PropTypes.func,
/**
* Event handler triggered when the component loses focus.
*/
onBlur: PropTypes.func,
} }


type Props = PropTypes.InferProps<typeof propTypes> type Props = PropTypes.InferProps<typeof propTypes>
@@ -133,10 +145,17 @@ type Props = PropTypes.InferProps<typeof propTypes>
* @see {@link RadioButton} for a similar component on selecting a single value among very few choices. * @see {@link RadioButton} for a similar component on selecting a single value among very few choices.
* @type {React.ComponentType<{readonly label?: string} & React.ClassAttributes<unknown>>} * @type {React.ComponentType<{readonly label?: string} & React.ClassAttributes<unknown>>}
*/ */
const Checkbox = React.forwardRef<HTMLInputElement, Props>(({ label = '', name, }, ref) => (
const Checkbox = React.forwardRef<HTMLInputElement, Props>(({ label = '', name, onChange, onFocus, onBlur }, ref) => (
<Base> <Base>
<CaptureArea> <CaptureArea>
<Input ref={ref} type="checkbox" name={name!} />
<Input
ref={ref}
type="checkbox"
name={name!}
onChange={onChange as React.ChangeEventHandler}
onFocus={onFocus as React.FocusEventHandler}
onBlur={onBlur as React.FocusEventHandler}
/>
<IndicatorWrapper> <IndicatorWrapper>
<Border /> <Border />
<Indicator> <Indicator>


+ 1
- 6
lib/components/Icon/Icon.tsx View File

@@ -43,12 +43,7 @@ const propTypes = {


type Props = PropTypes.InferProps<typeof propTypes> type Props = PropTypes.InferProps<typeof propTypes>


const Icon: React.FC<Props> = ({
name,
weight = '0.125rem',
size = '1.5rem',
label = name,
}) => {
const Icon: React.FC<Props> = ({ name, weight = '0.125rem', size = '1.5rem', label = name }) => {
const iconName = pascalCase(name, { transform: pascalCaseTransformMerge }) const iconName = pascalCase(name, { transform: pascalCaseTransformMerge })
const { [iconName as keyof typeof FeatherIcon]: TheIcon = null } = FeatherIcon const { [iconName as keyof typeof FeatherIcon]: TheIcon = null } = FeatherIcon
const { magnitude: sizeValue, unit: sizeUnit } = splitValueAndUnit(size) const { magnitude: sizeValue, unit: sizeUnit } = splitValueAndUnit(size)


+ 22
- 6
lib/components/RadioButton/RadioButton.tsx View File

@@ -121,6 +121,18 @@ const propTypes = {
* Short textual description indicating the nature of the component's value. * Short textual description indicating the nature of the component's value.
*/ */
label: PropTypes.any, label: PropTypes.any,
/**
* Event handler triggered when the component is selected.
*/
onChange: PropTypes.func,
/**
* Event handler triggered when the component receives focus.
*/
onFocus: PropTypes.func,
/**
* Event handler triggered when the component loses focus.
*/
onBlur: PropTypes.func,
} }


type Props = PropTypes.InferProps<typeof propTypes> type Props = PropTypes.InferProps<typeof propTypes>
@@ -132,13 +144,17 @@ type Props = PropTypes.InferProps<typeof propTypes>
* @type {React.ComponentType<{readonly label?: string, readonly name?: string} & React.ClassAttributes<unknown>>} * @type {React.ComponentType<{readonly label?: string, readonly name?: string} & React.ClassAttributes<unknown>>}
*/ */
const RadioButton = React.forwardRef<HTMLInputElement, Props>( const RadioButton = React.forwardRef<HTMLInputElement, Props>(
(
{ label = '', name, },
ref
) => (
({ label = '', name, onChange, onFocus, onBlur }, ref) => (
<Base> <Base>
<CaptureArea> <CaptureArea>
<Input ref={ref} name={name} type="radio" />
<Input
ref={ref}
name={name}
type="radio"
onChange={onChange as React.ChangeEventHandler}
onFocus={onFocus as React.FocusEventHandler}
onBlur={onBlur as React.FocusEventHandler}
/>
<IndicatorWrapper> <IndicatorWrapper>
<Border /> <Border />
<Indicator /> <Indicator />
@@ -149,7 +165,7 @@ const RadioButton = React.forwardRef<HTMLInputElement, Props>(
</Label> </Label>
</CaptureArea> </CaptureArea>
</Base> </Base>
)
),
) )


RadioButton.propTypes = propTypes RadioButton.propTypes = propTypes


+ 1
- 6
lib/components/Select/Select.tsx View File

@@ -242,12 +242,7 @@ const Select = React.forwardRef<HTMLSelectElement, Props>(
opacity: disabled ? 0.5 : undefined, opacity: disabled ? 0.5 : undefined,
}} }}
> >
{
border
&& (
<Border />
)
}
{border && <Border />}
<CaptureArea> <CaptureArea>
<LabelWrapper <LabelWrapper
style={{ style={{


+ 7
- 0
lib/components/Slider/Slider.tsx View File

@@ -204,6 +204,10 @@ const propTypes = {
* Is the component active? * Is the component active?
*/ */
disabled: PropTypes.bool, disabled: PropTypes.bool,
/**
* Event handler triggered when the component changes value.
*/
onChange: PropTypes.func,
} }


type Props = PropTypes.InferProps<typeof propTypes> type Props = PropTypes.InferProps<typeof propTypes>
@@ -226,6 +230,7 @@ const Slider: React.FC<Props> = ({
label = '', label = '',
length = '16rem', length = '16rem',
disabled = false, disabled = false,
onChange,
}) => { }) => {
const [isClient, setIsClient] = React.useState(false) const [isClient, setIsClient] = React.useState(false)


@@ -295,6 +300,7 @@ const Slider: React.FC<Props> = ({
padding: orientation === 'horizontal' ? '0.875rem 0.75rem' : '0.75rem 0.875rem', padding: orientation === 'horizontal' ? '0.875rem 0.75rem' : '0.75rem 0.875rem',
cursor: disabled ? 'not-allowed' : undefined, cursor: disabled ? 'not-allowed' : undefined,
}} }}
onChange={onChange!}
> >
<Track> <Track>
<Highlight <Highlight
@@ -361,6 +367,7 @@ const Slider: React.FC<Props> = ({
width: length!, width: length!,
}} }}
disabled={disabled!} disabled={disabled!}
onChange={onChange!}
type="range" type="range"
/> />
</ClickArea> </ClickArea>


+ 1
- 1
lib/components/TextInput/TextInput.test.tsx View File

@@ -70,7 +70,7 @@ describe.each`
multiline | tag multiline | tag
${false} | ${'input'} ${false} | ${'input'}
${true} | ${'textarea'} ${true} | ${'textarea'}
`('on multiline (multiline=$multiline)', ({ tag: rawTag, multiline: rawMultiline, }) => {
`('on multiline (multiline=$multiline)', ({ tag: rawTag, multiline: rawMultiline }) => {
const tag = rawTag as string const tag = rawTag as string
const multiline = rawMultiline as boolean const multiline = rawMultiline as boolean




+ 1
- 6
lib/components/TextInput/TextInput.tsx View File

@@ -279,12 +279,7 @@ const TextInput = React.forwardRef<HTMLInputElement | HTMLTextAreaElement, Props
opacity: disabled ? 0.5 : undefined, opacity: disabled ? 0.5 : undefined,
}} }}
> >
{
border
&& (
<Border />
)
}
{border && <Border />}
<CaptureArea> <CaptureArea>
<LabelWrapper <LabelWrapper
style={{ style={{


+ 11
- 3
package.json View File

@@ -1,6 +1,6 @@
{ {
"name": "@tesseract-design/react-common", "name": "@tesseract-design/react-common",
"version": "0.1.1",
"version": "0.2.0",
"description": "Common front-end components for Web using the Tesseract design system, written in React.", "description": "Common front-end components for Web using the Tesseract design system, written in React.",
"directories": { "directories": {
"lib": "dist" "lib": "dist"
@@ -37,7 +37,15 @@
"rollup-plugin-terser": "5.3.0", "rollup-plugin-terser": "5.3.0",
"ts-jest": "^26.1.3", "ts-jest": "^26.1.3",
"tslib": "^2.0.0", "tslib": "^2.0.0",
"typescript": "^3.9.7"
"typescript": "^3.9.7",
"@reach/slider": "^0.10.5",
"docz": "^2.3.1",
"pascal-case": "3.1.1",
"prop-types": "15.7.2",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-feather": "2.0.3",
"styled-components": "5.1.0"
}, },
"scripts": { "scripts": {
"prepublishOnly": "NODE_ENV=production rm -rf dist/ && rollup -c", "prepublishOnly": "NODE_ENV=production rm -rf dist/ && rollup -c",
@@ -46,7 +54,7 @@
"generate": "plop", "generate": "plop",
"docs": "docz" "docs": "docz"
}, },
"dependencies": {
"peerDependencies": {
"@reach/slider": "^0.10.5", "@reach/slider": "^0.10.5",
"docz": "^2.3.1", "docz": "^2.3.1",
"pascal-case": "3.1.1", "pascal-case": "3.1.1",


Loading…
Cancel
Save