Skip to content

Commit 0fd56cd

Browse files
committed
feat(buttonGroup): implement component
1 parent d689add commit 0fd56cd

File tree

33 files changed

+917
-2
lines changed

33 files changed

+917
-2
lines changed

packages/examples/accessibility-test/src/app/App.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
Badge,
55
Breadcrumb, BreadcrumbItem, BreadcrumbLink,
66
Button,
7+
ButtonGroup, ButtonGroupItem,
78
Card,
89
Checkbox, CheckboxControl, CheckboxLabel,
910
Clipboard, ClipboardControl, ClipboardTrigger,
@@ -249,6 +250,19 @@ function App(): ReactElement {
249250
<Button color={ BUTTON_COLOR.warning } size="sm" variant={ BUTTON_VARIANT.outline }>Warning</Button>
250251
</section>
251252

253+
<section>
254+
<h1>Button Group</h1>
255+
256+
<ButtonGroup>
257+
<ButtonGroupItem value="hourly">Hourly</ButtonGroupItem>
258+
<ButtonGroupItem value="daily">Daily</ButtonGroupItem>
259+
<ButtonGroupItem value="monthly">Monthly</ButtonGroupItem>
260+
<ButtonGroupItem value="custom">
261+
<Icon name={ ICON_NAME.calendar } /> Custom
262+
</ButtonGroupItem>
263+
</ButtonGroup>
264+
</section>
265+
252266
<section>
253267
<h1>Card</h1>
254268

Lines changed: 26 additions & 0 deletions
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;
Lines changed: 10 additions & 0 deletions
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+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { type Preview } from '@storybook/react';
2+
import '@ovhcloud/ods-themes/default/css';
3+
import '@ovhcloud/ods-themes/default/fonts';
4+
5+
const preview: Preview = {
6+
parameters: {},
7+
};
8+
9+
export default preview;
Lines changed: 17 additions & 0 deletions
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+
};
Lines changed: 26 additions & 0 deletions
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|tsx)$',
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+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declare module '*.css';
2+
declare module '*.scss';
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "@ovhcloud/ods-react-button-group",
3+
"version": "19.3.0",
4+
"private": true,
5+
"description": "ODS React ButtonGroup component",
6+
"type": "module",
7+
"main": "dist/index.js",
8+
"scripts": {
9+
"clean": "rimraf documentation node_modules",
10+
"doc": "npm run clean && npm run doc:ts && npm run doc:css",
11+
"doc:css": "sass src/components:documentation --no-source-map --pkg-importer=node && node ../../../scripts/generate-component-token-list.js",
12+
"doc:ts": "typedoc",
13+
"lint:a11y": "eslint --config ../../../../../.eslintrc-a11y 'src/**/*.{js,ts,tsx}' --ignore-pattern '*.stories.tsx'",
14+
"lint:scss": "stylelint --aei 'src/components/**/*.scss'",
15+
"lint:ts": "eslint '{src,tests}/**/*.{js,ts,tsx}' --ignore-pattern '*.stories.tsx'",
16+
"start": "npm run start:storybook",
17+
"start:storybook": "storybook dev -p 3000 --no-open",
18+
"test:e2e": "E2E=true start-server-and-test 'npm run start:storybook' 3000 'jest -i --detectOpenHandles'",
19+
"test:e2e:ci": "CI=true npm run test:e2e",
20+
"test:spec": "jest --passWithNoTests",
21+
"test:spec:ci": "npm run test:spec"
22+
}
23+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { ToggleGroup, useToggleGroupContext } from '@ark-ui/react/toggle-group';
2+
import classNames from 'classnames';
3+
import { type ComponentPropsWithRef, type FC, type JSX, forwardRef } from 'react';
4+
import { BUTTON_COLOR, BUTTON_VARIANT, Button } from '../../../../button/src';
5+
import { useButtonGroup } from '../../contexts/useButtonGroup';
6+
import style from './buttonGroupItem.module.scss';
7+
8+
interface ButtonGroupItemProp extends ComponentPropsWithRef<'button'> {
9+
/**
10+
* Whether the component is disabled.
11+
*/
12+
disabled?: boolean;
13+
/**
14+
* The value of the item.
15+
*/
16+
value: string,
17+
}
18+
19+
const ButtonGroupItem: FC<ButtonGroupItemProp> = forwardRef(({
20+
children,
21+
className,
22+
disabled,
23+
value,
24+
...props
25+
}, ref): JSX.Element => {
26+
const { value: selection } = useToggleGroupContext();
27+
const { size } = useButtonGroup();
28+
29+
return (
30+
<ToggleGroup.Item
31+
asChild
32+
className={ classNames(style['button-group-item'], className) }
33+
data-ods="button-group-item"
34+
disabled={ disabled }
35+
ref={ ref }
36+
value={ value }
37+
{ ...props }>
38+
<Button
39+
color={ BUTTON_COLOR.primary }
40+
size={ size }
41+
variant={ selection.indexOf(value) > -1 ? BUTTON_VARIANT.default : BUTTON_VARIANT.outline }>
42+
{ children }
43+
</Button>
44+
</ToggleGroup.Item>
45+
);
46+
});
47+
48+
ButtonGroupItem.displayName = 'ButtonGroupItem';
49+
50+
export {
51+
ButtonGroupItem,
52+
type ButtonGroupItemProp,
53+
};
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
$ods-button-group-item-z-index: 1;
2+
3+
@layer ods-molecules {
4+
.button-group-item {
5+
--ods-button-group-item-background-color-checked-disabled: var(--ods-color-neutral-500);
6+
7+
z-index: $ods-button-group-item-z-index;
8+
9+
&:first-child {
10+
margin-inline-start: 0;
11+
border-top-right-radius: 0;
12+
border-bottom-right-radius: 0;
13+
}
14+
15+
&:not(:first-child) {
16+
margin-inline-start: -1px;
17+
18+
&:not(:last-child) {
19+
border-radius: 0;
20+
}
21+
}
22+
23+
&:last-child {
24+
border-top-left-radius: 0;
25+
border-bottom-left-radius: 0;
26+
}
27+
28+
&:hover,
29+
&[data-focus],
30+
&[data-state="on"] {
31+
z-index: $ods-button-group-item-z-index + 1;
32+
}
33+
34+
&[data-state="on"] {
35+
&:not([data-disabled]) {
36+
border-color: var(--ods-theme-background-color-selected);
37+
background-color: var(--ods-theme-background-color-selected);
38+
}
39+
40+
&[data-disabled] {
41+
border-color: var(--ods-button-group-item-background-color-checked-disabled);
42+
background-color: var(--ods-button-group-item-background-color-checked-disabled);
43+
color: var(--ods-button-text-color-primary);
44+
}
45+
}
46+
47+
&[data-disabled] {
48+
z-index: $ods-button-group-item-z-index - 1;
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)