Skip to content

Commit

Permalink
refactor: ♻️ Textfield to use Field (#2710)
Browse files Browse the repository at this point in the history
resolves #2713

- Adds `DefaultProps` to `Field`
- Adds `counter` which takes a `number` or `FieldCounterProps`
- Kept `error` as we can look at this vs changes in `ValidationMessage`
later
- Will look closer at affix in #2748

---------

Co-authored-by: barsnes <[email protected]>
  • Loading branch information
mimarz and Barsnes authored Nov 8, 2024
1 parent 6b56db2 commit 5601aad
Show file tree
Hide file tree
Showing 21 changed files with 213 additions and 354 deletions.
5 changes: 5 additions & 0 deletions .changeset/eleven-experts-raise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet-react": patch
---

Textfield: Removed `htmlSize`, you can now use native `size`
5 changes: 5 additions & 0 deletions .changeset/loud-bobcats-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet-react": patch
---

Textfield: Refactored `characterLimit` to `counter` and now use new `Field.Counter` sub-component
5 changes: 5 additions & 0 deletions .changeset/olive-waves-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet-react": patch
---

Textfield: Now works as expected with `data-size`
5 changes: 5 additions & 0 deletions .changeset/popular-jeans-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet-react": patch
---

Textfield: Added `multiline` for switching between `input` and `textarea`
5 changes: 5 additions & 0 deletions .changeset/real-zoos-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet-react": patch
---

Textfield: Removed `hideLabel`, use `aria-label` or `aria-describedby` for "hidden" labels
5 changes: 5 additions & 0 deletions .changeset/witty-moose-scream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet-react": patch
---

Textfield: Update to use `Field` internally
6 changes: 5 additions & 1 deletion packages/css/field.css
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@
display: inline-flex; /* Using inline-flex to match native inline-block behaviour of <input> */
position: relative;
white-space: nowrap;
width: fit-content;
align-self: stretch; /* If ds-field is placed inside a flex container we need to fill width */

&:has(input[size]) {
width: fit-content;
}

& :not(.ds-input) {
padding-inline: var(--dsc-field-affix-padding-inline);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const WithForm: Story = () => (
<Textfield
label='Telefon'
id='telefon'
type='tel'
error='Telefonnummer kan kun inneholde siffer'
/>

Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/components/form/Field/Field.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ Det er viktig at samme informasjon som vises i prefixet eller suffixet også er

## Antall tegn

Bruk `Field.Counter` for å vise antall tegn som er skrevet i et tekstfelt.

<Canvas of={FieldStories.Counter} />
3 changes: 2 additions & 1 deletion packages/react/src/components/form/Field/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { useMergeRefs } from '@floating-ui/react';
import cl from 'clsx/lite';
import type { HTMLAttributes } from 'react';
import { forwardRef, useEffect, useRef } from 'react';
import type { DefaultProps } from '../../../types';
import { fieldObserver } from './fieldObserver';

export type FieldProps = HTMLAttributes<HTMLDivElement>;
export type FieldProps = HTMLAttributes<HTMLDivElement> & DefaultProps;
export const Field = forwardRef<HTMLDivElement, FieldProps>(function Field(
{ className, ...rest },
ref,
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/components/form/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { forwardRef } from 'react';
import type { DefaultProps } from '../../../types';

type InputAttr = InputHTMLAttributes<HTMLInputElement>;

export type InputProps = {
/** Supported `input` types */
type?: InputAttr['type'];
/** Defines the width of <Input> in count of characters.
/** Defines the width of `Input` in count of characters.
*/
size?: number;
/** Disables element
Expand Down
3 changes: 0 additions & 3 deletions packages/react/src/components/form/Textarea/Textarea.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ import * as TextareaStories from './Textarea.stories';
<Primary />
<Controls />

## Antall tegn

{/* <Canvas of={TextareaStories.WithCharacterCounter} /> */}

## Kontrollert

Expand Down
15 changes: 0 additions & 15 deletions packages/react/src/components/form/Textarea/Textarea.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,6 @@ export const Preview: Story = {
),
};

// export const WithCharacterCounter: Story = {
// args: {
// cols: 40,
// characterLimit: {
// maxCount: 5,
// },
// },
// render: (args) => (
// <>
// <Label>Label</Label>
// <Textarea {...args} />
// </>
// ),
// };

export const FullWidth: Story = {
args: {
cols: 40,
Expand Down
36 changes: 25 additions & 11 deletions packages/react/src/components/form/Textfield/Textfield.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,32 @@ import * as TextfieldStories from './Textfield.stories';

# Textfield

`Textfield` kalles tekstfelt på norsk. Det er et inndatafelt som for eksempel gir brukerne mulighet til å skrive korte tekster eller tall.
`Textfield` kalles tekstfelt på norsk. Det er et inndatafelt som for eksempel gir brukerne mulighet til å skrive korte tekster eller tall.

<br/>

- Du kan velge mellom både `input` og `textarea` for å lage tekstfelt.
- `Textfield` er en ferdig sammensatt `Field` komponent for standard oppsett av et tekstfelt.
- Ønsker du mer kontroll eller funksjonalitet, komponer med [Field](/docs/komponenter-field--docs) komponenten direkte.


<Primary />
<Controls />

## Slik bruker du Textfield

```tsx
import { Textfield } from '@digdir/designsystemet-react';

<Textfield label="Navn" />
```

## Multiline

Du endrer til `textarea` ved å sette `multiline` til `true`. Da kan brukerne skrive lengre tekster.

<Canvas of={TextfieldStories.Rows} />

## Prefix/Suffix

Prefixer og suffixer er nyttige for å vise enheter, valuta eller andre typer informasjon som er relevant for feltet.
Expand All @@ -22,17 +43,14 @@ Det er viktig at samme informasjon som vises i prefixet eller suffixet også er

## Antall tegn

<Canvas of={TextfieldStories.WithCharacterCounter} />
Bruk `counter` til å begrense antall tegn brukerne kan skrive i feltet.

<Canvas of={TextfieldStories.Counter} />

## Kontrollert

<Canvas of={TextfieldStories.Controlled} />

## Html size

`size` attributtet kan brukes for å angi bredden på tekstfeltet. Verdien kan være et tall fra 1 til 99.

<Canvas of={TextfieldStories.HtmlSize} />

## Retningslinjer for når du skal bruke `Textfield`

Expand Down Expand Up @@ -82,7 +100,3 @@ Ikke bruk deaktivert tilstand (disabled state) på tekstfelt. Tenk heller over o
### Prefiks og suffiks

Prefiks og suffiks er et ekstra visuelt hjelpemiddel, som blir ignorert av skjermlesere. Vi må alltid ha en beskrivende ledetekst. Prefiks og suffiks er plassert utenfor inndatafeltene de tilhører. Da unngår vi at de ikke skaper trøbbel i noen nettlesere, som kan sette inn et ikon i inndatafeltet (for eksempel ikoner for å vise eller lage passord).

## CSS Variabler

<CssVariables css={css} />
57 changes: 45 additions & 12 deletions packages/react/src/components/form/Textfield/Textfield.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,39 @@ type Story = StoryObj<typeof Textfield>;
export default {
title: 'Komponenter/Textfield',
component: Textfield,
argTypes: {
multiline: {
type: 'boolean',
},
// Using argType here to exclude values from React.HTMLInputTypeAttribute
type: {
control: 'select',
options: [
'checkbox',
'date',
'datetime-local',
'email',
'month',
'number',
'password',
'radio',
'search',
'tel',
'text',
'time',
'url',
'week',
// 'button',
'color',
'file',
// 'hidden',
// 'image',
// 'range',
// 'reset',
// 'submit',
],
},
},
} as Meta;

export const Preview: Story = {
Expand All @@ -18,38 +51,38 @@ export const Preview: Story = {
disabled: false,
readOnly: false,
'data-size': 'md',
multiline: false,
description: '',
error: '',
counter: 0,
},
};

export const WithCharacterCounter: Story = {
export const Rows: Story = {
args: {
label: 'Label',
characterLimit: {
maxCount: 5,
},
multiline: true,
rows: 4,
},
};

export const HtmlSize: Story = {
export const Adornments: Story = {
args: {
label: 'Label',
htmlSize: 10,
prefix: 'NOK',
suffix: 'pr. mnd',
label: 'Hvor mange kroner koster det per måned?',
},
};

export const Adornments: Story = {
export const Counter: Story = {
args: {
prefix: 'NOK',
suffix: 'pr. mnd',
'data-size': 'md',
counter: 10,
label: 'Hvor mange kroner koster det per måned?',
},
};

export const Controlled: StoryFn<typeof Textfield> = () => {
const [value, setValue] = useState<string>();
const [value, setValue] = useState<string>('');
return (
<>
<Textfield
Expand Down
60 changes: 7 additions & 53 deletions packages/react/src/components/form/Textfield/Textfield.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,78 +8,32 @@ import { Textfield } from './Textfield';
const user = userEvent.setup();

describe('Textfield', () => {
test('has correct value and label', () => {
it('has correct value and label', () => {
render({ value: 'test', label: 'label' });
expect(screen.getByLabelText('label')).toBeDefined();
expect(screen.getByDisplayValue('test')).toBeDefined();
});

test('has correct description', () => {
it('has correct description', () => {
render({ description: 'description' });
expect(
screen.getByRole('textbox', { description: 'description' }),
).toBeDefined();
});

test('has correct description and label when label is hidden', () => {
render({ description: 'description', label: 'label', hideLabel: true });

expect(screen.getByLabelText('label')).toBeDefined();
expect(
screen.getByRole('textbox', { description: 'description' }),
).toBeDefined();
it('should become a textarea when multiline is true', () => {
render({ multiline: true });
expect(screen.getByRole('textbox')).toBeInstanceOf(HTMLTextAreaElement);
});

test('is invalid with correct error message', () => {
it('is invalid with correct error message', () => {
render({ error: 'error-message' });

const input = screen.getByRole('textbox', { description: 'error-message' });
expect(input).toBeDefined();
expect(input).toBeInvalid();
});

test('is invalid with correct error message from errorId', () => {
renderRtl(
<>
<span id='my-error'>my error message</span>
<Textfield errorId='my-error' error />
</>,
);

const input = screen.getByRole('textbox', {
description: 'my error message',
});
expect(input).toBeDefined();
expect(input).toBeInvalid();
});

it('should have max allowed characters label for screen readers', () => {
render({
characterLimit: {
maxCount: 10,
srLabel: 'Max 10 characters is allowed',
label: (count: number) => `${count} characters remaining`,
},
});
const screenReaderText = screen.getByText('Max 10 characters is allowed');
expect(screenReaderText).toBeInTheDocument();
});

it('should countdown remaining characters', async () => {
const user = userEvent.setup();
render({
label: 'First name',
characterLimit: {
maxCount: 10,
label: (count: number) => `${count} characters remaining`,
srLabel: 'characters remaining',
},
});
const inputField = screen.getByLabelText('First name');
await act(async () => await user.type(inputField, 'Peter'));
expect(screen.getByText('5 characters remaining')).toBeInTheDocument();
});

it('Triggers onBlur event when field loses focus', async () => {
const onBlur = vi.fn();
render({ onBlur });
Expand Down Expand Up @@ -133,7 +87,7 @@ describe('Textfield', () => {
});
});

const render = (props: Partial<TextfieldProps> = {}) =>
const render = (props: TextfieldProps = {}) =>
renderRtl(
<Textfield
{...{
Expand Down
Loading

0 comments on commit 5601aad

Please sign in to comment.