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 3ad059a commit 0fc9082
Show file tree
Hide file tree
Showing 23 changed files with 502 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/ods/react/tests/_app/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const componentNames = [
'password',
'popover',
'progress-bar',
'form-field',
//--generator-anchor--
];

Expand Down
10 changes: 10 additions & 0 deletions packages/ods/react/tests/_app/src/components/ods-form-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react-dom/client';
import { OdsFormField } from 'ods-components-react';

const FormField = () => {
return (
<OdsFormField />
);
};

export default FormField;
23 changes: 23 additions & 0 deletions packages/ods/react/tests/e2e/ods-form-field.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Page } from 'puppeteer';
import { goToComponentPage, setupBrowser } from '../setup';

describe('ods-form-field react', () => {
const setup = setupBrowser();
let page: Page;

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

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

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

// TODO add relevant tests

expect(false).toBe(true);
});
});
5 changes: 5 additions & 0 deletions packages/ods/src/components/form-field/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Local Stencil command generates external ods component build at the root of the project
# Excluding them is a temporary solution to avoid pushing generated files
# But the issue may cause main build (ods-component package) to fails, as it detects multiples occurences
# of the same component and thus you have to delete all those generated dir manually
*/src/
19 changes: 19 additions & 0 deletions packages/ods/src/components/form-field/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@ovhcloud/ods-component-form-field",
"version": "17.1.0",
"private": true,
"description": "ODS FormField component",
"main": "dist/index.cjs.js",
"collection": "dist/collection/collection-manifest.json",
"scripts": {
"clean": "rimraf .stencil coverage dist docs-api www",
"doc": "typedoc --pretty --plugin ../../../scripts/typedoc-plugin-decorator.js && node ../../../scripts/generate-typedoc-md.js",
"lint:scss": "stylelint 'src/components/**/*.scss'",
"lint:ts": "eslint '{src,tests}/**/*.{js,ts,tsx}'",
"start": "stencil build --dev --watch --serve",
"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"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@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;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 4px;
width: 100%;
}

&__bottom-bar {
margin-top: 4px;

&__error-message {
color: var(--ods-color-critical-500);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { FunctionalComponent } from '@stencil/core';
import { Component, Host, Prop, h } from '@stencil/core';
import { ODS_TEXT_PRESET } from '../../../../text/src';

@Component({
shadow: true,
styleUrl: 'ods-form-field.scss',
tag: 'ods-form-field',
})
export class OdsFormField {
@Prop({ reflect: true }) public error: string = '';

render(): FunctionalComponent {
return (
<Host class='ods-form-field'>
<div class='ods-form-field__top-bar'>
<slot name='label'></slot>
<slot name='visual-hint'></slot>
</div>
<slot></slot>
<div class='ods-form-field__bottom-bar'>
{
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>
: <slot name='helper'></slot>
}
</div>
</Host>
);
}
}
12 changes: 12 additions & 0 deletions packages/ods/src/components/form-field/src/globals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Import here all the external ODS component that you need to run the current component
* when running dev server (yarn start) or e2e tests
*
* ex:
* import '../../text/src';
*/
import '../../text/src';
import '../../icon/src';
import '../../input/src';
import '../../textarea/src';
import '../../tooltip/src';
135 changes: 135 additions & 0 deletions packages/ods/src/components/form-field/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<!DOCTYPE html>
<html dir='ltr' lang='en'>
<head>
<meta charset='utf-8' />
<meta name='viewport' content='width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0' />
<title>Dev ods-form-field</title>

<script type='module' src='/build/ods-form-field.esm.js'></script>
<script nomodule src='/build/ods-form-field.js'></script>
<link rel="stylesheet" href="/build/ods-form-field.css">
</head>

<body>

<p>1 (Label only)</p>
<ods-form-field>
<ods-text slot="label" preset='label'>
<ods-icon name="home"></ods-icon>
A custom label with an icon
</ods-text>
<ods-input type="text" id="midable"></ods-input>
</ods-form-field>

<div class="divider"></div>
<p>2 (Label & Visual Hint)</p>
<ods-form-field>
<ods-text slot="label" preset='label'>
Description
</ods-text>
<ods-text slot="visual-hint" preset='caption'>
150/200
</ods-text>
<ods-input type="text" id="midable"></ods-input>
</ods-form-field>

<div class="divider"></div>
<p>3 (Label, Visual Hint & Helper)</p>
<ods-form-field>
<ods-text slot="label" preset='label'>
Description
</ods-text>

<ods-text slot="visual-hint" preset='caption'>
150/200
</ods-text>

<ods-input type="text" value="Hello, ODS!" clearable></ods-input>

<ods-text slot="helper" preset='span'>
A little helper text
</ods-text>
</ods-form-field>

<div class="divider"></div>
<p>4 (Label, Visual Hint & Error)</p>
<ods-form-field error="Wrong format.">
<ods-text slot="label" preset='label'>
Description
</ods-text>

<ods-text slot="visual-hint" preset='caption'>
150/200
</ods-text>

<ods-input type="text" value="Hello, ODS!" clearable></ods-input>

<ods-text slot="helper" preset='span'>
A little helper text
</ods-text>
</ods-form-field>

<div class="divider"></div>
<p>5 (With a Tooltip)</p>
<ods-form-field>
<ods-text slot="label" preset='label'>
Description
</ods-text>

<ods-text id="tooltip-trigger" preset='caption' slot="visual-hint">
(?)
</ods-text>

<ods-tooltip trigger-id="tooltip-trigger">
150/200
</ods-tooltip>

<ods-textarea></ods-textarea>

<ods-text preset='span' slot="helper">
Write a few sentences.
</ods-text>
</ods-form-field>

<div class="divider"></div>
<p>6 (Too long text)</p>
<ods-form-field>
<ods-text slot="label" preset='label'>
Description
</ods-text>

<ods-text id="tooltip-trigger2" preset='span' slot="visual-hint">
Need help?
</ods-text>

<ods-tooltip trigger-id="tooltip-trigger2">
150/200
</ods-tooltip>

<ods-textarea class="textarea"></ods-textarea>

<ods-text preset='span' slot="helper">
Write a few sentences about you and the others.
</ods-text>
</ods-form-field>

<div class="divider"></div>
</body>
</html>

<style>
* {
font-family: 'Source Sans Pro', sans-serif;
}

.divider {
width: 100%;
height: 1px;
margin-top: 48px;
background: lightgray;
}

.textarea::part(textarea) {
width: 100%;
}
</style>
1 change: 1 addition & 0 deletions packages/ods/src/components/form-field/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { OdsFormField } from './components/ods-form-field/ods-form-field';
7 changes: 7 additions & 0 deletions packages/ods/src/components/form-field/stencil.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { getStencilConfig } from '../../config/stencil';

export const config = getStencilConfig({
args: process.argv.slice(2),
componentCorePackage: '@ovhcloud/ods-component-form-field',
namespace: 'ods-form-field',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { E2EElement, E2EPage } from '@stencil/core/testing';
import { newE2EPage } from '@stencil/core/testing';

describe('ods-form-field rendering', () => {
let el: E2EElement;
let page: E2EPage;

async function setup(content: string, customStyle?: string): Promise<void> {
page = await newE2EPage();

await page.setContent(content);
await page.evaluate(() => document.body.style.setProperty('margin', '0px'));

if (customStyle) {
await page.addStyleTag({ content: customStyle });
}

el = await page.find('ods-form-field');
}

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

expect(el.shadowRoot).not.toBeNull();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { SpecPage } from '@stencil/core/testing';
import { newSpecPage } from '@stencil/core/testing';
import { OdsFormField } from '../../src';

describe('ods-form-field rendering', () => {
let page: SpecPage;
let root: HTMLElement | undefined;

async function setup(html: string): Promise<void> {
page = await newSpecPage({
components: [OdsFormField],
html,
});

root = page.root;
}

describe('error', () => {
it('should be reflected', async() => {
const errorValue = 'Wrong format.';

await setup(`<ods-form-field error="${errorValue}"></ods-form-field>`);

expect(root?.getAttribute('error')).toBe(errorValue);
});

it('should not be set by default', async() => {
await setup('<ods-form-field></ods-form-field>');

expect(root?.getAttribute('error')).toBeNull();
});
});
});
7 changes: 7 additions & 0 deletions packages/ods/src/components/form-field/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../../../tsconfig.json",
"include": [
"src",
"tests"
]
}
10 changes: 10 additions & 0 deletions packages/ods/src/components/form-field/typedoc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"entryPoints": ["src/index.ts"],
"excludeInternal": true,
"excludePrivate": true,
"excludeProtected": true,
"hideGenerator": true,
"json": "dist/docs-api/typedoc.json",
"out": "dist/docs-api/",
"tsconfig":"tsconfig.json"
}
1 change: 1 addition & 0 deletions packages/ods/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ export * from './code/src';
export * from './password/src';
export * from './popover/src';
export * from './progress-bar/src';
export * from './form-field/src';
1 change: 1 addition & 0 deletions packages/ods/vue/tests/_app/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const componentNames = [
'password',
'popover',
'progress-bar',
'form-field',
//--generator-anchor--
];

Expand Down
Loading

0 comments on commit 0fc9082

Please sign in to comment.