reactjs - React Storybook -valintaruudun muutoskäsittelijä ei toimi


Olen luomassa valintaruudun komponenttia käyttäen Reactia, Typescriptiä, Storybookia ja Styled-Componentsia, seurattuani tätä opasta: building-a-checkbox-component-with-react-and-styled-components. Olen joutunut säätämään koodia, koska käytän FunctionComponentia, mutta kohtaan ongelman muutoksen käsittelijän kanssa. En voi valita tai poistaa valintaa valintaruudusta, joka näyttää olevan vain-lukuinen, enkä ole varma, missä teen virheen. Tässä on koodini:

Checkbox.tsx

import React from 'react';
import styled from 'styled-components';
import { FunctionComponent } from 'react';

type CheckboxProps = {
    checked?: boolean;
    onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
};

const CheckboxContainer = styled.div`
    display: inline-block;
    vertical-align: middle;
`;

const Icon = styled.svg`
    fill: none;
    stroke: white;
    stroke-width: 2px;
`;

const HiddenCheckbox = styled.input.attrs({ type: 'checkbox' })`
    border: 0;
    clip: rect(0 0 0 0);
    clippath: inset(50%);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    padding: 0;
    position: absolute;
    white-space: nowrap;
    width: 1px;
`;

const StyledCheckbox = styled.div`
    display: inline-block;
    width: 16px;
    height: 16px;
    background: ${(props: CheckboxProps) => (props.checked ? 'green' : 'white')};
    border-color: 'dark gray';
    border-width: 1px;
    border-style: solid;
    border-radius: 2px;

    ${HiddenCheckbox}:focus + & {
        box-shadow: 0 0 0 3px grey;
    }

    ${Icon} {
        visibility: ${(props: CheckboxProps) => (props.checked ? 'visible' : 'hidden')};
    }
`;

const Checkbox: FunctionComponent<CheckboxProps> = ({ onChange, checked, ...props }) => {
    return (
        <CheckboxContainer>
            <HiddenCheckbox checked={checked} {...props} onChange={onChange} />
            <StyledCheckbox data-testid="styledcheckbox" checked={checked} {...props} onChange={onChange}>
                <Icon viewBox="0 0 24 24">
                    <polyline points="20 6 9 17 4 12" />
                </Icon>
            </StyledCheckbox>
        </CheckboxContainer>
    );
};

export default Checkbox;

Checkbox.stories.js

// Checkbox.stories.js
import React, { useState, useRef } from 'react';
import Checkbox from '@components/Checkbox/Checkbox';
import { action } from '@storybook/addon-actions';
import { storiesOf } from '@storybook/react';

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
const CheckboxStateful = props => {
    const [value, setValue] = useState(props);
    const valRef = useRef(value);
    // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
    const onChange = event => {
        setValue(event.target.value);
        valRef.current = event.target.value;
    };

    return (
        <Checkbox
            value={value}
            onChange={event => {
                onChange(event);
            }}
        ></Checkbox>
    );
};
storiesOf('Checkbox', module)
    .add('with checked', () => {
        const value = true;
        // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
        const onChange = event => setValue(event.target.value);
        return <CheckboxStateful value={value} onChange={onChange}></CheckboxStateful>;
    })
    .add('with unchecked', () => {
        const value = false;
        // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
        const onChange = event => setValue(event.target.value);
        return <CheckboxStateful value={value} onChange={onChange}></CheckboxStateful>;
    });

Koska olen aloittelija sekä Reactissa että Storybookissa, saatankin tarvita asiantuntijan apua arvioidakseni, onko muutoksen käsittelijäkoodini oikea vai ei. Olen katsonut muita esimerkkejä, mutta mikään niistä ei käytä Typescriptiä. Kaikki apu olisi arvostettua.

Vastaus #1

Ratkaisu on vaihtaa Checkbox.tsx-tiedoston CheckboxPropsissa onChange onClickiksi seuraavasti:

onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;

ja yksinkertaistaa CheckboxStateful Checkbox.stories.js-tiedostossa seuraavasti:

const CheckboxStateful = () => {
const [value, setValue] = useState(false);
return <Checkbox checked={value} onClick={() => setValue(!value)}></Checkbox>;

};

Täysin päivitetty koodi on nyt seuraava:

Checkbox.tsx

import * as React from 'react';
import styled from 'styled-components';
import { FunctionComponent } from 'react';

type CheckboxProps = {
    checked?: boolean;
    onClick: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
};

const CheckboxContainer = styled.div`
    display: inline-block;
    vertical-align: middle;
`;

const Icon = styled.svg`
    fill: none;
    stroke: white;
    stroke-width: 2px;
`;

const HiddenCheckbox = styled.input.attrs({ type: 'checkbox' })`
    border: 0;
    clip: rect(0 0 0 0);
    clippath: inset(50%);
    height: 1px;
    margin: -1px;
    overflow: hidden;
    padding: 0;
    position: absolute;
    white-space: nowrap;
    width: 1px;
`;

const StyledCheckbox = styled.div`
    display: inline-block;
    width: 16px;
    height: 16px;
    background: ${(props: CheckboxProps) => (props.checked ? 'green' : 'white')};
    border-color: 'dark gray';
    border-width: 1px;
    border-style: solid;
    border-radius: 2px;

    ${HiddenCheckbox}:focus + & {
        box-shadow: 0 0 0 3px grey;
    }

    ${Icon} {
        visibility: ${(props: CheckboxProps) => (props.checked ? 'visible' : 'hidden')};
    }
`;

const Checkbox: FunctionComponent<CheckboxProps> = ({ onClick, checked, ...props }) => {
    return (
        <CheckboxContainer>
            <HiddenCheckbox checked={checked} {...props} />
            <StyledCheckbox checked={checked} {...props} onClick={onClick} data-testid="styledcheckbox">
                <Icon viewBox="0 0 24 24">
                    <polyline points="20 6 9 17 4 12" />
                </Icon>
            </StyledCheckbox>
        </CheckboxContainer>
    );
};

export default Checkbox;

Checkbox.stories.js

/* eslint-disable @typescript-eslint/explicit-function-return-type */
// Checkbox.stories.js
import * as React from 'react';
import { useState } from 'react';
import Checkbox from '@components/Checkbox/Checkbox';
import { action } from '@storybook/addon-actions';
import { storiesOf } from '@storybook/react';

export default {
    title: 'Checkbox',
    component: Checkbox,
};

const CheckboxStateful = () => {
    const [value, setValue] = useState(false);
    return <Checkbox checked={value} onClick={() => setValue(!value)}></Checkbox>;
};

storiesOf('Checkbox', module)
    .add('with checked', () => {
        const value = true;
        return <Checkbox checked={value} onClick={action('checked')}></Checkbox>;
    })
    .add('with unchecked', () => {
        const value = false;
        return <Checkbox checked={value} onClick={action('checked')}></Checkbox>;
    })
    .add('stateful', () => {
        return <CheckboxStateful />;
    });

Kun olet luonut React-projektin ja lisännyt Storybookin, suorita vain yarn storybook ja valintaruudut näkyvät oikein.

Vastaus #2

Uuden Stateful-komponentin luomisen sijaan paljon parempi lähestymistapa on käyttää tilaa komponentin templatessa.

Esimerkki:

const Template: ComponentStory<typeof MyComponent> = (args) => {
  const [state, setState] = useState('');

  return (
    <MyComponent
      prop1={args.prop1}
      prop2={args.prop2}
      onChange={() => setState('Change')}
    />
  );
};

Tässä on linkki alkuperäiseen vastaukseen, josta sain tämän lähestymistavan: https://stackoverflow.com/a/64722559/10641401