Skip to content

Commit

Permalink
refactor(radio): implementation component
Browse files Browse the repository at this point in the history
  • Loading branch information
aesteves60 authored and dpellier committed Jul 29, 2024
1 parent a2cff7e commit 98ab900
Show file tree
Hide file tree
Showing 29 changed files with 1,310 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 @@ -31,6 +31,7 @@ const componentNames = [
'progress-bar',
'form-field',
'message',
'radio',
//--generator-anchor--
];

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

const Radio = () => {
function onOdsChange() {
console.log('React radio odsChange');
}

return (
<>
<OdsRadio onOdsChange={ onOdsChange } />

<OdsRadio isDisabled
onOdsChange={ onOdsChange } />
</>
);
};

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

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

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

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

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

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

it('trigger the odsChange handler on type', async () => {
const elem = await page.$('ods-radio:not([is-disabled])');
let consoleLog = '';
page.on('console', (consoleObj) => {
consoleLog = consoleObj.text();
});

await elem?.click();
// Small delay to ensure page console event has been resolved
await new Promise((resolve) => setTimeout(resolve, 100));

expect(consoleLog).toBe('React radio odsChange');
});

it('does not trigger the odsChange handler on type if disabled', async () => {
const elem = await page.$('ods-radio[is-disabled]');
let consoleLog = '';
page.on('console', (consoleObj) => {
consoleLog = consoleObj.text();
});

await elem?.click();
// Small delay to ensure page console event has been resolved
await new Promise((resolve) => setTimeout(resolve, 100));

expect(consoleLog).toBe('');
});
});
1 change: 1 addition & 0 deletions packages/ods/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ export * from './popover/src';
export * from './progress-bar/src';
export * from './form-field/src';
export * from './message/src';
export * from './radio/src';
5 changes: 5 additions & 0 deletions packages/ods/src/components/radio/.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/radio/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@ovhcloud/ods-component-radio",
"version": "17.1.0",
"private": true,
"description": "ODS Radio 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 --max-workers=2",
"test:e2e:ci": "tsc --noEmit && stencil test --e2e --ci --config stencil.config.ts --max-workers=2",
"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,7 @@
@use '../../../../../style/radio';

.ods-radio {
&__radio {
@include radio.ods-radio();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import type { OdsRadioValueChangeEventDetail } from '../../interfaces/events';
import { Component, Element, Event, type EventEmitter, type FunctionalComponent, Host, Method, Prop, h } from '@stencil/core';

@Component({
formAssociated: true,
shadow: false,
styleUrl: 'ods-radio.scss',
tag: 'ods-radio',
})
export class OdsRadio {
private inputEl?: HTMLInputElement;

@Element() el!: HTMLElement;

@Prop({ reflect: true }) public ariaLabel: HTMLElement['ariaLabel'] = null;
@Prop({ reflect: true }) public ariaLabelledby?: string;
@Prop({ reflect: true }) public isChecked: boolean = false;
@Prop({ reflect: true }) public isDisabled: boolean = false;
@Prop({ reflect: true }) public isRequired: boolean = false;
@Prop({ reflect: true }) public inputId?: string;
@Prop({ reflect: true }) public name!: string;
@Prop({ mutable: true, reflect: true }) public value: string | null = null;

@Event() odsBlur!: EventEmitter<void>;
@Event() odsChange!: EventEmitter<OdsRadioValueChangeEventDetail>;
@Event() odsClear!: EventEmitter<void>;
@Event() odsFocus!: EventEmitter<void>;

@Method()
async clear(): Promise<void> {
if (this.inputEl) {
this.inputEl.checked = false;
}
this.odsClear.emit();
}

@Method()
async getValidity(): Promise<ValidityState | undefined> {
return this.inputEl?.validity;
}

private onInput(): void {
this.odsChange.emit({
name: this.name,
validity: this.inputEl?.validity,
value: this.value ?? null,
});
}

render(): FunctionalComponent {
return (
<Host class="ods-radio">
<input
aria-label={ this.ariaLabel }
aria-labelledby={ this.ariaLabelledby }
class="ods-radio__radio"
checked={ this.isChecked }
disabled={ this.isDisabled }
onBlur={ (): CustomEvent<void> => this.odsBlur.emit() }
onFocus={ (): CustomEvent<void> => this.odsFocus.emit() }
onInput={ (): void => this.onInput() }
id={ this.inputId }
name={ this.name }
ref={ (el): HTMLInputElement => this.inputEl = el as HTMLInputElement }
required={ this.isRequired }
type="radio"
value={ this.value?.toString() || '' } />
</Host>
);
}
}
7 changes: 7 additions & 0 deletions packages/ods/src/components/radio/src/controller/ods-radio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
function setFormValue(internals: ElementInternals, value: number | string | null): void {
internals.setFormValue(value?.toString() ?? '');
}

export {
setFormValue,
};
7 changes: 7 additions & 0 deletions packages/ods/src/components/radio/src/globals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* 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';
*/
110 changes: 110 additions & 0 deletions packages/ods/src/components/radio/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<!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-radio</title>

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

<body>
<p>Default & Methods</p>
<div>
<ods-radio input-id="huey" name="drone" value="huey"></ods-radio>
<label for="huey">Huey</label>
</div>

<div>
<ods-radio input-id="dewey" name="drone" value="dewey"></ods-radio>
<label for="dewey">Dewey</label>
</div>

<div>
<ods-radio input-id="louie" name="drone" value="louie"></ods-radio>
<label for="louie">Louie</label>
</div>
<button id="clear-button">clear</button>
<button id="get-validity-button">getValidity</button>
<script>
const radio = document.querySelector('ods-radio[input-id="huey"]');

['odsBlur', 'odsClear', 'odsFocus', 'odsReset', 'odsChange'].forEach((eventName) =>{
radio.addEventListener(eventName, (event) => {
console.log(eventName, event);
});
});
const clearButton = document.getElementById('clear-button');
const getValidityButton = document.getElementById('get-validity-button');

clearButton.addEventListener('click', () => radio.clear());
getValidityButton.addEventListener('click', async () => {
const validity = await radio.getValidity();
console.log('validity', validity);
});
</script>

<p>Multiligne label</p>
<div>
<ods-radio input-id="multiligne" name="multiligne" value="multiligne"></ods-radio>
<label for="multiligne"> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</label>
</div>

<p>Disabled</p>
<div>
<ods-radio is-disabled is-checked input-id="checked" name="disabled" value="checked"></ods-radio>
<label for="checked">Check</label>
</div>
<div>
<ods-radio is-disabled input-id="not-checked" name="disabled" value="not-checked"></ods-radio>
<label for="not-checked">Not Check</label>
</div>

<p>Custom CSS</p>
<ods-radio class="my-radio" input-id="custom-css" name="custom-css" value="custom-css">
</ods-radio>
<label for="custom-css">Custom CSS</label>
<style>
.my-radio > input[type="radio"]:not(:disabled):checked {
background-color: red;
border-color: red;
}
</style>

<p>Form</p>
<form id="radio-form" target="_self">
<ods-input name="ods-input" value="On Vous Heberge ?" clearable></ods-input>
<div>
<ods-radio input-id="huey-form" name="name" value="huey"></ods-radio>
<label for="huey-form">Huey</label>
</div>

<div>
<ods-radio input-id="dewey-form" name="name" value="dewey"></ods-radio>
<label for="dewey-form">Dewey</label>
</div>

<div>
<ods-radio input-id="louie-form" name="name" value="louie"></ods-radio>
<label for="louie-form">Louie</label>
</div>

<input type="text" name="natif-input">
<button id="form-reset-button" type="reset">Reset</button>
<button id="form-submit-button" type="submit">Submit</button>
</form>

<script>
const form = document.getElementById('radio-form');
const resetFormButton = document.getElementById('form-reset-button');
const submitFormButton = document.getElementById('form-submit-button');

submitFormButton.addEventListener('click', () => {
const formData = new FormData(form);
console.log('formData', formData);
});
</script>
</body>
</html>
2 changes: 2 additions & 0 deletions packages/ods/src/components/radio/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { OdsRadio } from './components/ods-radio/ods-radio';
export { type OdsRadioValueChangeEvent, type OdsRadioValueChangeEventDetail } from './interfaces/events';
12 changes: 12 additions & 0 deletions packages/ods/src/components/radio/src/interfaces/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
interface OdsRadioValueChangeEventDetail {
name: string;
validity?: ValidityState;
value: string | null;
}

type OdsRadioValueChangeEvent = CustomEvent<OdsRadioValueChangeEventDetail>;

export {
type OdsRadioValueChangeEvent,
type OdsRadioValueChangeEventDetail,
};
7 changes: 7 additions & 0 deletions packages/ods/src/components/radio/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-radio',
namespace: 'ods-radio',
});
Loading

0 comments on commit 98ab900

Please sign in to comment.