-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui): input text repasse technique et tests manquant (#2110)
* feat(UI): création d'un input * doc: Ajoute une story * feat: Ajoute du style sur l'Input * feat(UI): ajout de usetouched * feat(UI): au mounted check validation * fix: Fix le type de validation dans la story * refacto(input): retours review finale --------- Co-authored-by: Gauthier Fiorentino <[email protected]> Co-authored-by: Suxue LI <[email protected]>
- Loading branch information
1 parent
a079e77
commit cbbd6fb
Showing
8 changed files
with
348 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
@use "@styles/utilities"; | ||
|
||
$color-text: utilities.$color-text-primary; | ||
$color-border: utilities.$color-background-border; | ||
$color-text-disabled: inherit; | ||
$color-background-disabled: utilities.$color-background-disabled; | ||
$color-error: utilities.$color-error; | ||
$error-border-width: 2px; | ||
$border-radius: 1.25rem; | ||
$border-width: 1px; | ||
$border-width-compensation: calc($error-border-width - $border-width); | ||
|
||
%input { | ||
color: $color-text; | ||
background-color: transparent; | ||
padding: 0.5rem 1rem; | ||
border-radius: $border-radius; | ||
border: $border-width solid $color-border; | ||
|
||
&:disabled, | ||
&:read-only { | ||
color: $color-text-disabled; | ||
background-color: $color-background-disabled; | ||
cursor: not-allowed; | ||
} | ||
|
||
&[data-touched="true"]:invalid { | ||
border-color: $color-error; | ||
border-width: $error-border-width; | ||
} | ||
|
||
&:valid, | ||
&:not([data-touched="true"]) { | ||
// NOTE (GAFI 06-07-2023): Compense la bordure plus épaisse quand en erreur | ||
margin: $border-width-compensation 0 $border-width-compensation $border-width-compensation; | ||
} | ||
} | ||
|
||
.input { | ||
@extend %input; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { Meta, StoryObj } from '@storybook/react'; | ||
import { ComponentPropsWithoutRef } from 'react'; | ||
|
||
import { Input } from './Input'; | ||
|
||
const meta: Meta<typeof Input> = { | ||
component: Input, | ||
title: 'Components/Form/Input', | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof Input>; | ||
export const exemple: Story = { | ||
args: {}, | ||
render: (args) => ( | ||
<> | ||
<label htmlFor="pays">Pays</label> | ||
<Input id="pays" {...args} /> | ||
</> | ||
), | ||
}; | ||
|
||
export const disabled: Story = { | ||
args: { | ||
disabled: true, | ||
}, | ||
render: (args) => ( | ||
<> | ||
<label htmlFor="pays">Pays</label> | ||
<Input id="pays" {...args} /> | ||
</> | ||
), | ||
}; | ||
|
||
export const withValidation: Story = { | ||
args: { | ||
validation: (value: ComponentPropsWithoutRef<typeof Input>['value']) => | ||
(typeof value === 'string' && value === 'France') ? '' : 'Entrer "France"', | ||
}, | ||
render: (args) => ( | ||
<> | ||
<label htmlFor="pays">Pays (Entrer France)</label> | ||
<Input id="pays" {...args}/> | ||
</> | ||
), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
/** | ||
* @jest-environment jsdom | ||
*/ | ||
|
||
import { render, screen } from '@testing-library/react'; | ||
import { userEvent } from '@testing-library/user-event'; | ||
|
||
import { Input } from '~/client/components/ui/Form/InputText/Input'; | ||
|
||
describe('<Input/>', () => { | ||
it('affiche un textbox', () => { | ||
render(<Input/>); | ||
expect(screen.getByRole('textbox')).toBeVisible(); | ||
}); | ||
|
||
it('accepte les props natives d‘un input', () => { | ||
render(<Input disabled aria-label={'foo'}/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
expect(input).toBeDisabled(); | ||
expect(input).toHaveAccessibleName('foo'); | ||
}); | ||
|
||
it('accepte une ref', () => { | ||
const ref = jest.fn(); | ||
render(<Input ref={ref}/>); | ||
|
||
expect(ref).toHaveBeenCalledTimes(1); | ||
expect(ref).toHaveBeenCalledWith(expect.any(HTMLInputElement)); | ||
}); | ||
|
||
it('accepte une classe', () => { | ||
render(<Input className={'className'}/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
expect(input).toHaveAttribute('class', expect.stringContaining('className')); | ||
}); | ||
|
||
it('accepte un onChange', async () => { | ||
const onChange = jest.fn(); | ||
const user = userEvent.setup(); | ||
render(<Input onChange={onChange}/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
await user.type(input, 'a'); | ||
expect(onChange).toHaveBeenCalledTimes(1); | ||
expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ target: input })); | ||
}); | ||
|
||
it('accepte un onFocus', async () => { | ||
const onFocus = jest.fn(); | ||
const user = userEvent.setup(); | ||
render(<Input onFocus={onFocus}/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
await user.click(input); | ||
expect(onFocus).toHaveBeenCalledTimes(1); | ||
expect(onFocus).toHaveBeenCalledWith(expect.objectContaining({ target: input })); | ||
}); | ||
|
||
it('accepte un onBlur', async () => { | ||
const onBlur = jest.fn(); | ||
const user = userEvent.setup(); | ||
render(<Input onBlur={onBlur}/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
await user.type(input, 'a'); | ||
await user.tab(); | ||
expect(onBlur).toHaveBeenCalledTimes(1); | ||
expect(onBlur).toHaveBeenCalledWith(expect.objectContaining({ target: input })); | ||
}); | ||
|
||
describe('validation', () => { | ||
it('lorsque la valeur initiale de l‘input est valide, l‘input est valide', async () => { | ||
const validation = jest.fn().mockReturnValue(''); | ||
render(<Input validation={validation}/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
expect(input).toBeValid(); | ||
}); | ||
|
||
it('lorsque la valeur initiale de l‘input n‘est pas valide, l‘input est invalide', async () => { | ||
const validation = jest.fn().mockReturnValue('error message'); | ||
render(<Input validation={validation}/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
expect(input).toBeInvalid(); | ||
}); | ||
|
||
it('lorsque je tape une valeur valide, l‘input est valide', async () => { | ||
const validation = jest.fn().mockReturnValue(''); | ||
const user = userEvent.setup(); | ||
render(<Input validation={validation}/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
await user.type(input, 'a'); | ||
|
||
expect(input).toBeValid(); | ||
}); | ||
|
||
it('lorsque je tape une valeur invalide, l‘input est en erreur', async () => { | ||
const validation = jest.fn().mockReturnValue('error'); | ||
const user = userEvent.setup(); | ||
render(<Input validation={validation}/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
await user.type(input, 'a'); | ||
|
||
expect(input).toBeInvalid(); | ||
}); | ||
}); | ||
|
||
describe('l’input est marqué comme touché ou non', () => { | ||
it('n’est pas marqué comme touché par défaut', () => { | ||
render(<Input/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
expect(input).toHaveAttribute('data-touched', 'false'); | ||
}); | ||
|
||
it('marque le champ comme touché quand on quitte le champ après avoir écrit dedans', async () => { | ||
const user = userEvent.setup(); | ||
render(<Input/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
await user.type(input, 'a'); | ||
await user.tab(); | ||
|
||
expect(input).toHaveAttribute('data-touched', 'true'); | ||
}); | ||
|
||
it('ne marque pas le champ tant qu’on ne quitte pas le champ', async () => { | ||
const user = userEvent.setup(); | ||
render(<Input/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
await user.type(input, 'a'); | ||
|
||
expect(input).toHaveAttribute('data-touched', 'false'); | ||
}); | ||
|
||
it('ne marque pas le champ comme touché quand on quitte le champ sans avoir écrit dedans', async () => { | ||
const user = userEvent.setup(); | ||
render(<Input/>); | ||
|
||
const input = screen.getByRole('textbox'); | ||
await user.click(input); | ||
await user.tab(); | ||
|
||
expect(input).toHaveAttribute('data-touched', 'false'); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.