Skip to content

Commit 2996336

Browse files
committed
feat(card): adds card component to react
1 parent 4ee91f2 commit 2996336

21 files changed

+450
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { StorybookConfig } from '@storybook/react-vite';
2+
3+
const config: StorybookConfig = {
4+
core: {
5+
disableTelemetry: true,
6+
disableWhatsNewNotifications: true,
7+
},
8+
docs: {
9+
autodocs: false,
10+
},
11+
framework: '@storybook/react-vite',
12+
previewHead: (head) => `
13+
${head}
14+
<style>
15+
html, body {
16+
font-family: "Source Sans Pro", "Trebuchet MS", "Arial", "Segoe UI", sans-serif;
17+
}
18+
</style>
19+
`,
20+
stories: [
21+
'../src/dev.stories.tsx',
22+
'../tests/**/*.stories.tsx',
23+
],
24+
};
25+
26+
export default config;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { addons } from '@storybook/manager-api';
2+
3+
addons.register('custom-panel', (api) => {
4+
api.togglePanel(false);
5+
});
6+
7+
addons.setConfig({
8+
enableShortcuts: false,
9+
showToolbar: true,
10+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { type Preview } from '@storybook/react';
2+
import '@ovhcloud/ods-themes/default';
3+
4+
const preview: Preview = {
5+
parameters: {},
6+
};
7+
8+
export default preview;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const isCI = !!process.env.CI;
2+
3+
export default {
4+
launch: {
5+
headless: isCI,
6+
slowMo: isCI ? 0 : 300,
7+
product: 'chrome',
8+
args: [
9+
'--no-sandbox',
10+
'--disable-setuid-sandbox',
11+
"--disable-dev-shm-usage",
12+
"--disable-accelerated-2d-canvas",
13+
"--disable-gpu",
14+
'--font-render-hinting=none',
15+
],
16+
},
17+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const baseOption = {
2+
collectCoverage: false,
3+
testPathIgnorePatterns: [
4+
'node_modules/',
5+
'dist/',
6+
],
7+
testRegex: 'tests\\/.*\\.spec\\.ts$',
8+
transform: {
9+
'\\.(ts|tsx)$': 'ts-jest',
10+
},
11+
verbose: true,
12+
};
13+
14+
export default !!process.env.E2E ?
15+
{
16+
...baseOption,
17+
preset: 'jest-puppeteer',
18+
testRegex: 'tests\\/.*\\.e2e\\.ts$',
19+
testTimeout: 60000,
20+
} : {
21+
...baseOption,
22+
transform: {
23+
...baseOption.transform,
24+
'\\.scss$': 'jest-transform-stub',
25+
}
26+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
declare module '*.scss';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "@ovhcloud/ods-react-card",
3+
"version": "18.6.1",
4+
"private": true,
5+
"description": "ODS React Card component",
6+
"type": "module",
7+
"main": "dist/index.js",
8+
"scripts": {
9+
"clean": "rimraf documentation node_modules",
10+
"doc": "typedoc",
11+
"lint:a11y": "eslint --config ../../../../../.eslintrc-a11y 'src/**/*.{js,ts,tsx}' --ignore-pattern '*.stories.tsx'",
12+
"lint:scss": "stylelint 'src/components/**/*.scss'",
13+
"lint:ts": "eslint '{src,tests}/**/*.{js,ts,tsx}' --ignore-pattern '*.stories.tsx'",
14+
"start": "npm run start:storybook",
15+
"start:storybook": "storybook dev -p 3000 --no-open",
16+
"test:e2e": "E2E=true start-server-and-test 'npm run start:storybook' 3000 'jest -i --detectOpenHandles'",
17+
"test:e2e:ci": "CI=true npm run test:e2e",
18+
"test:spec": "jest 'tests/.*.spec.ts$' --passWithNoTests",
19+
"test:spec:ci": "npm run test:spec"
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import classNames from 'classnames';
2+
import { type ComponentPropsWithRef, type FC, type JSX, forwardRef } from 'react';
3+
import { ODS_CARD_COLOR, type OdsCardColor } from '../../constants/card-color';
4+
import style from './odsCard.module.scss';
5+
6+
interface OdsCardProp extends ComponentPropsWithRef<'div'> {
7+
color?: OdsCardColor,
8+
}
9+
10+
const OdsCard: FC<OdsCardProp> = forwardRef(({
11+
className,
12+
color = ODS_CARD_COLOR.primary,
13+
...props
14+
}, ref): JSX.Element => {
15+
return (
16+
<div
17+
className={ classNames(
18+
style['ods-card'],
19+
style[`ods-card--${color}`],
20+
className
21+
) }
22+
ref={ ref }
23+
{ ...props }>
24+
</div>
25+
);
26+
});
27+
28+
OdsCard.displayName = 'OdsCard';
29+
30+
export {
31+
OdsCard,
32+
type OdsCardProp,
33+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.ods-card {
2+
box-sizing: border-box;
3+
display: inline-block;
4+
border: 1px solid;
5+
border-radius: 8px;
6+
7+
&--neutral {
8+
border-color: var(--ods-color-neutral-200);
9+
}
10+
11+
&--primary {
12+
border-color: var(--ods-color-primary-200);
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
enum ODS_CARD_COLOR {
2+
neutral = 'neutral',
3+
primary = 'primary',
4+
}
5+
6+
type OdsCardColor = `${ODS_CARD_COLOR}`;
7+
8+
const ODS_CARD_COLORS = Object.freeze(Object.values(ODS_CARD_COLOR));
9+
10+
export {
11+
ODS_CARD_COLOR,
12+
ODS_CARD_COLORS,
13+
type OdsCardColor,
14+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { OdsCard } from '.';
2+
3+
export default {
4+
component: OdsCard,
5+
title: 'OdsCard dev',
6+
};
7+
8+
export const Default = () => (
9+
<OdsCard />
10+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { OdsCard, type OdsCardProp } from './components/ods-card/OdsCard';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import 'jest-puppeteer';
2+
import { gotoStory } from '../../../../helpers/test';
3+
4+
describe('OdsCard rendering', () => {
5+
it('should render the web component', async() => {
6+
await gotoStory(page, 'rendering/render');
7+
8+
expect(await page.waitForSelector('[data-testid="render"]')).not.toBeNull();
9+
});
10+
11+
describe('custom style', () => {
12+
it('should render with custom style applied', async() => {
13+
await gotoStory(page, 'rendering/custom-style');
14+
15+
const odsCard = await page.waitForSelector('[data-testid="custom-style"]');
16+
const height = await odsCard?.evaluate((el: Element) => el.getBoundingClientRect().height);
17+
18+
expect(height).toBe(42);
19+
});
20+
});
21+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { OdsCard } from '../../src';
2+
3+
export default {
4+
component: OdsCard,
5+
title: 'Tests rendering',
6+
};
7+
8+
export const customStyle = () => (
9+
<OdsCard
10+
data-testid="custom-style"
11+
style={{ height: '42px' }} />
12+
);
13+
14+
export const render = () => (
15+
<OdsCard data-testid="render" />
16+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"extends": "../../../tsconfig.json",
3+
"include": ["modules.d.ts", "src", "tests"],
4+
"exclude": [".storybook", "node_modules"]
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"disableGit": true,
3+
"disableSources": true,
4+
"entryPoints": ["src/index.ts"],
5+
"excludeExternals": true,
6+
"excludeInternal": true,
7+
"excludePrivate": true,
8+
"excludeProtected": true,
9+
"outputs": [
10+
{
11+
"name": "json",
12+
"path": "./documentation/ods-card.json"
13+
}
14+
],
15+
"tsconfig":"tsconfig.json"
16+
}

packages/ods-react/src/components/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ export * from './popover/src';
88
export * from './tooltip/src';
99
export * from './textarea/src';
1010
export * from './form-field/src';
11+
12+
export * from './card/src';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { type Meta, type StoryObj } from '@storybook/react';
2+
import React from 'react';
3+
import { OdsCard, type OdsCardProp } from '../../../../ods-react/src/components/card/src';
4+
import { CONTROL_CATEGORY } from '../../../src/constants/controls';
5+
import { orderControls } from '../../../src/helpers/controls';
6+
7+
type Story = StoryObj<OdsCardProp>;
8+
9+
const meta: Meta<OdsCardProp> = {
10+
component: OdsCard,
11+
// @ts-ignore see https://github.com/storybookjs/storybook/issues/27535
12+
// subcomponents: { OdsCardXxx }, // Uncomment if sub components, otherwise remove
13+
title: 'ODS Components/Card',
14+
};
15+
16+
export default meta;
17+
18+
export const Demo: Story = {
19+
argTypes: orderControls({
20+
color: {
21+
table: {
22+
category: CONTROL_CATEGORY.design,
23+
defaultValue: { summary: 'primary' },
24+
type: { summary: 'string' }
25+
},
26+
control: 'select',
27+
options: ['primary', 'neutral'],
28+
},
29+
children: {
30+
table: {
31+
category: CONTROL_CATEGORY.slot,
32+
defaultValue: { summary: 'ø' },
33+
},
34+
control: 'text',
35+
},
36+
}),
37+
args: {
38+
color: 'primary',
39+
children: 'Hello, world!',
40+
},
41+
};
42+
43+
export const Default: Story = {
44+
tags: ['!dev'],
45+
render: () => (
46+
<OdsCard>
47+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br />Interdum et malesuada fames ac ante ipsum primis in faucibus.</p>
48+
</OdsCard>
49+
),
50+
};
51+
52+
export const Color: Story = {
53+
tags: ['!dev'],
54+
render: () => (
55+
<div style={{ display: 'flex', gap: '16px' }}>
56+
<OdsCard color="primary">
57+
<p>Primary Card</p>
58+
</OdsCard>
59+
<OdsCard color="neutral">
60+
<p>Neutral Card</p>
61+
</OdsCard>
62+
</div>
63+
),
64+
};
65+
66+
export const CustomCSS: Story = {
67+
tags: ['!dev'],
68+
render: () => (
69+
<OdsCard style={{ width: '500px', display: 'flex', justifyContent: 'center', border: '3px solid green' }}>
70+
<p>Custom styled card</p>
71+
</OdsCard>
72+
),
73+
};

0 commit comments

Comments
 (0)