Skip to content

Commit

Permalink
feat(form-field): implement component
Browse files Browse the repository at this point in the history
  • Loading branch information
Leotheluck authored and dpellier committed Jul 29, 2024
1 parent 0fc9082 commit 1b7940c
Show file tree
Hide file tree
Showing 12 changed files with 371 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import React from 'react-dom/client';
import { OdsFormField } from 'ods-components-react';
import { OdsFormField, OdsInput, OdsText } from 'ods-components-react';

const FormField = () => {
return (
<OdsFormField />
<OdsFormField error="Wrong format.">
<OdsText slot="label" preset='label'>Description</OdsText>
<OdsText slot="visual-hint" preset='caption'>02/11/1999</OdsText>
<OdsInput name='input' />
<OdsText slot="helper" preset='span'>A little helper text</OdsText>
</OdsFormField>
);
};

Expand Down
42 changes: 37 additions & 5 deletions packages/ods/react/tests/e2e/ods-form-field.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,51 @@ describe('ods-form-field react', () => {
const setup = setupBrowser();
let page: Page;

beforeAll(async () => {
beforeAll(async() => {
page = setup().page;
});

beforeEach(async () => {
beforeEach(async() => {
await goToComponentPage(page, 'ods-form-field');
});

it('render the component correctly', async () => {
it('renders the component correctly', async() => {
const elem = await page.$('ods-form-field');
const boundingBox = await elem?.boundingBox();

// TODO add relevant tests
expect(boundingBox?.height).toBeGreaterThan(0);
expect(boundingBox?.width).toBeGreaterThan(0);
});

it('properly renders the label slot', async() => {
const elem = await page.$('ods-form-field');
const label = await elem?.evaluate((el) => el.querySelector('ods-text[slot="label"]')?.textContent);
expect(label).toBe('Description');
});

it('properly renders the visual-hint slot', async() => {
const elem = await page.$('ods-form-field');
const visualHint = await elem?.evaluate((el) => el.querySelector('ods-text[slot="visual-hint"]')?.textContent);
expect(visualHint).toBe('02/11/1999');
});

expect(false).toBe(true);
it('properly renders the default slot', async() => {
const elem = await page.$('ods-form-field > ods-input');
const boundingBox = await elem?.boundingBox();

expect(boundingBox?.height).toBeGreaterThan(0);
expect(boundingBox?.width).toBeGreaterThan(0);
});

it('properly renders the helper slot', async() => {
const elem = await page.$('ods-form-field');
const helper = await elem?.evaluate((el) => el.querySelector('ods-text[slot="helper"]')?.textContent);
expect(helper).toBe('A little helper text');
});

it('displays the error message when error attribute is set', async() => {
const elem = await page.$('ods-form-field');
const errorMessage = await elem?.evaluate((el) => el.shadowRoot?.querySelector('ods-text.ods-form-field__bottom-bar__error-message')?.textContent);
expect(errorMessage).toBe('Wrong format.');
});
});
3 changes: 2 additions & 1 deletion packages/ods/src/components/form-field/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"test:e2e": "stencil test --e2e --config stencil.config.ts",
"test:e2e:ci": "tsc --noEmit && stencil test --e2e --ci --config stencil.config.ts",
"test:spec": "stencil test --spec --config stencil.config.ts --coverage",
"test:spec:ci": "tsc --noEmit && stencil test --config stencil.config.ts --spec --ci --coverage"
"test:spec:ci": "tsc --noEmit && stencil test --config stencil.config.ts --spec --ci --coverage",
"toto": "jest"
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
@use '../../../../../style/input';

:host(.ods-form-field) {
display: inline-flex;
position: relative;
flex-direction: column;
}

.ods-form-field {

&__top-bar {
display: flex;
flex-direction: row;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ export class OdsFormField {
<slot></slot>
<div class='ods-form-field__bottom-bar'>
{
this.error
&& this.error.length > 0
this.error &&
this.error.length > 0
? <ods-text
class='ods-form-field__bottom-bar__error-message'
preset={ ODS_TEXT_PRESET.span }
>
{ this.error }
</ods-text>
class='ods-form-field__bottom-bar__error-message'
preset={ ODS_TEXT_PRESET.span }
>
{ this.error }
</ods-text>
: <slot name='helper'></slot>
}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,60 @@ describe('ods-form-field rendering', () => {
}

it('should render the web component', async() => {
await setup('<ods-form-field></ods-form-field>');

await setup('<ods-form-field><ods-input></ods-input></ods-form-field>');
expect(el.shadowRoot).not.toBeNull();
});

it('should display an error message when error attribute is set', async() => {
await setup('<ods-form-field error="Wrong format."><ods-input></ods-input></ods-form-field>');
const errorMessage = await page.find('ods-form-field >>> .ods-form-field__bottom-bar__error-message');
expect(errorMessage).not.toBeNull();
expect(errorMessage?.textContent).toBe('Wrong format.');
});

it('should not display error message when error is empty', async() => {
await setup('<ods-form-field error=""><ods-input></ods-input></ods-form-field>');
const errorMessage = await page.find('ods-form-field >>> .ods-form-field__bottom-bar__error-message');
expect(errorMessage).toBeNull();
});

it('should correctly render content in the label slot', async() => {
await setup('<ods-form-field><ods-input></ods-input><span slot="label">Label</span></ods-form-field>');
const labelSlot = await el.find('span[slot="label"]');
expect(labelSlot).not.toBeNull();
expect(labelSlot.textContent).toEqual('Label');
});

it('should correctly render content in the visual-hint slot', async() => {
await setup('<ods-form-field><ods-input></ods-input><span slot="visual-hint">Visual Hint</span></ods-form-field>');
const visualHintSlot = await el.find('span[slot="visual-hint"]');
expect(visualHintSlot).not.toBeNull();
expect(visualHintSlot.textContent).toEqual('Visual Hint');
});

it('should correctly render content in the default slot', async() => {
await setup('<ods-form-field><ods-input></ods-input></ods-form-field>');
const input = await el.find('ods-input');
expect(input).not.toBeNull();
});

it('should correctly render alternative content in the default slot', async() => {
await setup('<ods-form-field><ods-textarea></ods-textarea></ods-form-field>');
const textarea = await el.find('ods-textarea');
expect(textarea).not.toBeNull();
});

it('should correctly render helper text in the helper slot if no error', async() => {
await setup('<ods-form-field><ods-input></ods-input><span slot="helper">Helper</span></ods-form-field>');
const helperSlot = await el.find('span[slot="helper"]');
expect(helperSlot).not.toBeNull();
expect(helperSlot.textContent).toEqual('Helper');
});

it('should be displaying the error message if error, even if there is a helper', async() => {
await setup('<ods-form-field error="Wrong format."><ods-input></ods-input><span slot="helper">Helper Text</span></ods-form-field>');
const errorMessage = await page.find('ods-form-field >>> .ods-form-field__bottom-bar__error-message');
expect(errorMessage).not.toBeNull();
expect(errorMessage?.textContent).toEqual('Wrong format.');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('ods-form-field rendering', () => {
it('should not be set by default', async() => {
await setup('<ods-form-field></ods-form-field>');

expect(root?.getAttribute('error')).toBeNull();
expect(root?.getAttribute('error')).toBe('');
});
});
});
11 changes: 9 additions & 2 deletions packages/ods/vue/tests/_app/src/components/ods-form-field.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
<template>
<OdsFormField />
<OdsFormField>
<OdsText slot="label" preset='label'>Description</OdsText>
<OdsText slot="visual-hint" preset='caption'>02/11/1999</OdsText>
<OdsInput name='input' />
<OdsText slot="helper" preset='span'>A little helper text</OdsText>
</OdsFormField>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { OdsFormField } from '@ovhcloud/ods-components/vue';
import { OdsFormField, OdsInput, OdsText } from '@ovhcloud/ods-components/vue';
export default defineComponent({
name: 'FormField',
components: {
OdsFormField,
OdsInput,
OdsText
},
});
</script>
36 changes: 31 additions & 5 deletions packages/ods/vue/tests/e2e/ods-form-field.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,45 @@ describe('ods-form-field vue', () => {
const setup = setupBrowser();
let page: Page;

beforeAll(async () => {
beforeAll(async() => {
page = setup().page;
});

beforeEach(async () => {
beforeEach(async() => {
await goToComponentPage(page, 'ods-form-field');
});

it('render the component correctly', async () => {
it('renders the component correctly', async() => {
const elem = await page.$('ods-form-field');
const boundingBox = await elem?.boundingBox();

// TODO add relevant tests
expect(boundingBox?.height).toBeGreaterThan(0);
expect(boundingBox?.width).toBeGreaterThan(0);
});

it('properly renders the label slot', async() => {
const elem = await page.$('ods-form-field');
const label = await elem?.evaluate((el) => el.querySelector('ods-text[slot="label"]')?.textContent);
expect(label).toBe('Description');
});

expect(false).toBe(true);
it('properly renders the visual-hint slot', async() => {
const elem = await page.$('ods-form-field');
const visualHint = await elem?.evaluate((el) => el.querySelector('ods-text[slot="visual-hint"]')?.textContent);
expect(visualHint).toBe('02/11/1999');
});

it('properly renders the default slot', async() => {
const elem = await page.$('ods-form-field > ods-input');
const boundingBox = await elem?.boundingBox();

expect(boundingBox?.height).toBeGreaterThan(0);
expect(boundingBox?.width).toBeGreaterThan(0);
});

it('properly renders the helper slot', async() => {
const elem = await page.$('ods-form-field');
const helper = await elem?.evaluate((el) => el.querySelector('ods-text[slot="helper"]')?.textContent);
expect(helper).toBe('A little helper text');
});
});
25 changes: 19 additions & 6 deletions packages/storybook/stories/components/form-field/documentation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import * as FormFieldStories from './form-field.stories';

# Overview

TODO Some text about the component
A Form Field component is used to wrap several form components with logic, visual hints and additional styling:

<Canvas of={ FormFieldStories.Default } sourceState='none' />

<DocNavigator of={ FormFieldStories } />

<Markdown>{ SpecificationsFormField }</Markdown>

# Style customization [IF RELEVANT]
# Style customization

You can add your own style on the form-field element using the part `form-field`.
You can add your own style on the form-field elements using `::part` or by directly updating the slot elements.

Custom form-field css:

Expand All @@ -32,7 +32,20 @@ Custom form-field css:

<Canvas of={ FormFieldStories.Default } sourceState='shown' />

## [Other attribute]
<Canvas of={ FormFieldStories.Default } sourceState='shown' />
## Error
<Canvas of={ FormFieldStories.Error } sourceState='shown' />

## Label only
<Canvas of={ FormFieldStories.Label } sourceState='shown' />

## Label & Visual Hint
<Canvas of={ FormFieldStories.LabelVisualHint } sourceState='shown' />

## Label, Visual Hint & Helper
<Canvas of={ FormFieldStories.LabelVisualHintHelper } sourceState='shown' />

## Tooltip
<Canvas of={ FormFieldStories.Tooltip } sourceState='shown' />

...
## Textarea
<Canvas of={ FormFieldStories.Textarea } sourceState='shown' />
Loading

0 comments on commit 1b7940c

Please sign in to comment.