Skip to content

Commit

Permalink
feat(icon): implement component
Browse files Browse the repository at this point in the history
  • Loading branch information
dpellier committed Jul 29, 2024
1 parent e7d3178 commit b4d27ee
Show file tree
Hide file tree
Showing 33 changed files with 539 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 @@ -9,6 +9,7 @@
const componentNames = [
'spinner',
'text',
'icon',
//--generator-anchor--
];

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

const Icon = () => {
return (
<OdsIcon name={ ODS_ICON_NAME.warning } />
);
};

export default Icon;
1 change: 1 addition & 0 deletions packages/ods/react/tests/_app/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { createRoot } from 'react-dom/client';
import { StrictMode, Suspense, lazy } from 'react';
import { BrowserRouter, Link, Route, Routes } from 'react-router-dom';
import { componentNames } from './components';
import '@ovhcloud/ods-themes/default';

const root = createRoot(document.getElementById('root')!);

Expand Down
23 changes: 23 additions & 0 deletions packages/ods/react/tests/e2e/ods-icon.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-icon react', () => {
const setup = setupBrowser();
let page: Page;

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

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

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

expect(boundingBox?.height).toBeGreaterThan(0);
expect(boundingBox?.width).toBeGreaterThan(0);
});
});
7 changes: 7 additions & 0 deletions packages/ods/src/components/icon/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# List of things to be done before releasing this component

- update `documentation/migration.from.17.x.md` with missing info from design (search fro TODO in the file)
- update icon name list when available
- move implementation to fonts when file server get available
- lastly, when all is done, remove this file
-
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
## Attributes changes

`ariaName` <img src="https://img.shields.io/badge/updated-00FFFF" />

Has been updated.

You can use the new `alt` attribute to obtain the same behavior.

`color` <img src="https://img.shields.io/badge/removed-FF0000" />

Has been removed.

Icons are now font, they will by default have the same color as the current text. (TODO will be true after font update only)

You can override this using a css class on the component directly.

`contrasted` <img src="https://img.shields.io/badge/removed-FF0000" />

Has been removed.

Icons are now font, they will by default have the same color as the current text. (TODO will be true after font update only)

`hoverable` <img src="https://img.shields.io/badge/removed-FF0000" />

Has been removed.

Interactive icon will be handled specifically by components that implements those interactions.

TODO? add example of scss mixin to reproduce current style?

`size` <img src="https://img.shields.io/badge/removed-FF0000" />

Has been removed.

Icons are now font, they will by default have the same size as the current text. (TODO will be true after font update only)

You can override this using a css class on the component directly.

## Migration examples

Contrasted icon:
```html
<ods-icon contrasted
name="warning">
</ods-icon>
<!-- is now -->
<style>
.custom {
color: #fff;
}
</style>

<ods-icon class="custom"
name="warning">
</ods-icon>
```

Icon size:
```html
<ods-icon name="warning"
size="xl">
</ods-icon>
<!-- is now -->
<style>
.custom {
font-size: 48px;
}
</style>

<ods-icon class="custom"
name="warning">
</ods-icon>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Table of Contents
[• Properties](#properties)



[• Enums](#enums)

## Properties
### alt

`Optional` **alt**: [_string_] = `''`


### name

**name**: [`arrow-left` | `warning`]




## Enums
## Enumeration: ODS_ICON_NAME

### arrowLeft

**arrowLeft** = `"arrow-left"`

### warning

**warning** = `"warning"`

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Markdown } from '@storybook/addon-docs';
import Specs from './spec.md';

## Description

<Markdown>{ Specs }</Markdown>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Canvas } from '@storybook/addon-docs';

<Canvas sourceState="none">
<osds-icon name='arrow-left'></osds-icon>
<osds-icon name='warning'></osds-icon>
</Canvas >

```html
<osds-icon name='arrow-left'></osds-icon>
<osds-icon name='warning'></osds-icon>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import NameProperty from './properties/usage.name.mdx';

## Usage

### Name
<NameProperty />
19 changes: 19 additions & 0 deletions packages/ods/src/components/icon/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@ovhcloud/ods-component-icon",
"version": "17.1.0",
"private": true,
"description": "ODS Icon 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/**/*.{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"
}
}
4 changes: 4 additions & 0 deletions packages/ods/src/components/icon/src/assets/icons.data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"arrow-left": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='%23000000' fill-rule='evenodd' d='M22.207 12.207a1 1 0 01-1 1H5.621L9.914 17.5A1 1 0 018.5 18.914l-6.707-6.707.7-.7.015-.015L8.5 5.5a1 1 0 011.414 1.414l-4.29 4.292 15.583.001a1 1 0 011 1z'/%3E%3C/svg%3E",
"warning": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='%23000000' fill-rule='evenodd' d='M13.014 3.248a2 2 0 01.71.71l8.251 14.028A2 2 0 0120.252 21H3.748a2 2 0 01-1.723-3.014l8.251-14.028a2 2 0 012.738-.71zM12 4.972L3.748 19h16.504L12 4.972zm.068 12.025h.017a1.006 1.006 0 01-.052.002L12 17l.068-.003zM12 17a1.018 1.018 0 01-.367-.07l-.056-.024a1.016 1.016 0 01-.198-.122l-.088-.079-.05-.056.048.055-.05-.055-.026-.032-.022-.029a1.045 1.045 0 01-.154-.318.959.959 0 01-.025-.116l-.008-.069L11 16a1.018 1.018 0 01.037-.27l.012-.04a.96.96 0 01.067-.158l.034-.059a1.05 1.05 0 01.063-.09l.078-.088.064-.059.011-.01.001-.001.001-.001.05-.038a1.042 1.042 0 01.32-.151.933.933 0 01.112-.024l.067-.008L12 15h.02a1.006 1.006 0 01.183.02l.058.014a1.069 1.069 0 01.106.035l.056.025a1.016 1.016 0 01.194.119l.09.08a1.008 1.008 0 01.219.329l.025.07a1.11 1.11 0 01.037.154l.008.068.002.069v-.043l.001.028v.066l-.003.051a.985.985 0 01-.033.185l-.012.04a.96.96 0 01-.164.307l-.08.09a1.008 1.008 0 01-.447.259l-.056.013a1.099 1.099 0 01-.056.01l-.066.007h-.002l-.005.001h-.007l.011-.001L12 17zm-.634-1.774l-.01.01-.063.057.06-.056.013-.011zm.553-.224l-.002.001-.006.001.008-.002zM12 15l-.069.002h.015L11.98 15H12zm0-6a1 1 0 011 1v3a1 1 0 01-2 0v-3a1 1 0 011-1zm-.622 6.217l-.01.007v.001l.01-.008z'/%3E%3C/svg%3E"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// TODO replace all this with dedicated icon font when file server get available
:host(.ods-icon) {
display: inline-block;
mask: center/contain no-repeat;
mask-image: var(--ods-icon-mask-image, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'/%3E"));
background-color: var(--ods-color-primary-500);
width: 1rem;
height: 1rem;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { OdsIconName } from '../../constants/icon-name';
import type { FunctionalComponent } from '@stencil/core';
import { Component, Host, Prop, h } from '@stencil/core';
import icons from '../../assets/icons.data.json'; // TODO replace with dedicated icon font when file server get available

@Component({
shadow: true,
styleUrl: 'ods-icon.scss',
tag: 'ods-icon',
})
export class OdsIcon {
@Prop({ reflect: true }) alt?: string = '';
@Prop({ reflect: true }) name!: OdsIconName;

render(): FunctionalComponent {
const base64Icon = icons[this.name];

return (
<Host
class="ods-icon"
alt={ this.alt }
style={
(base64Icon ? { '--ods-icon-mask-image': `url("${base64Icon}")` } : { })
}
></Host>
);
}
}
17 changes: 17 additions & 0 deletions packages/ods/src/components/icon/src/constants/icon-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
enum ODS_ICON_NAME {
// TODO need actual list from design
arrowLeft = 'arrow-left',
warning = 'warning',
}

type OdsIconName =`${ODS_ICON_NAME}`;

const ODS_ICON_NAMES = Object.freeze(Object.values(ODS_ICON_NAME));

export type {
OdsIconName,
};
export {
ODS_ICON_NAME,
ODS_ICON_NAMES,
};
7 changes: 7 additions & 0 deletions packages/ods/src/components/icon/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';
*/
46 changes: 46 additions & 0 deletions packages/ods/src/components/icon/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!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-icon</title>

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

<!-- TODO use font-size and color when switching to fonts -->
<style>
.custom {
width: 48px;
height: 48px;
background-color: orange;
}
</style>
</head>

<body>
<div>
<h3>All set</h3>

<ods-icon alt="warning"
name="warning">
</ods-icon>
</div>

<div>
<h3>No alt</h3>

<ods-icon name="arrow-left">
</ods-icon>
</div>

<div>
<h3>Custom style</h3>

<ods-icon class="custom"
name="arrow-left">
</ods-icon>
</div>
</body>
</html>
3 changes: 3 additions & 0 deletions packages/ods/src/components/icon/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type { OdsIconName } from './constants/icon-name';
export { ODS_ICON_NAME, ODS_ICON_NAMES } from './constants/icon-name';
export { OdsIcon } from './components/ods-icon/ods-icon';
7 changes: 7 additions & 0 deletions packages/ods/src/components/icon/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-icon',
namespace: 'ods-icon',
});
38 changes: 38 additions & 0 deletions packages/ods/src/components/icon/tests/rendering/ods-icon.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { E2EElement, E2EPage } from '@stencil/core/testing';
import { newE2EPage } from '@stencil/core/testing';
import { ODS_ICON_NAME } from '../../src';

describe('ods-icon 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-icon');
}

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

expect(el.shadowRoot).not.toBeNull();
});

describe('custom style', () => {
it('should render with custom style applied', async() => {
const customHeight = 200;

await setup(`<ods-icon name="${ODS_ICON_NAME.warning}"></ods-icon>`, `ods-icon { height: ${customHeight}px; }`);

const elStyle = await el.getComputedStyle();
expect(parseInt(elStyle.getPropertyValue('height'), 10)).toBe(customHeight);
});
});
});
Loading

0 comments on commit b4d27ee

Please sign in to comment.