Skip to content

Commit 283b0f1

Browse files
committed
feat(buttonGroup): implement component
1 parent d689add commit 283b0f1

File tree

32 files changed

+903
-2
lines changed

32 files changed

+903
-2
lines changed
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+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { ToggleGroup } from '@ark-ui/react/toggle-group';
2+
import classNames from 'classnames';
3+
import { type FC, type JSX, forwardRef } from 'react';
4+
import { ButtonGroupProvider, type ButtonGroupRootProp } from '../../contexts/useButtonGroup';
5+
import style from './buttonGroup.module.scss';
6+
7+
interface ButtonGroupProp extends ButtonGroupRootProp {}
8+
9+
const ButtonGroup: FC<ButtonGroupProp> = forwardRef(({
10+
children,
11+
className,
12+
defaultValue,
13+
disabled,
14+
multiple,
15+
onValueChange,
16+
size,
17+
value,
18+
...props
19+
}, ref): JSX.Element => {
20+
return (
21+
<ButtonGroupProvider size={ size }>
22+
<ToggleGroup.Root
23+
className={ classNames(style['button-group'], className) }
24+
data-ods="button-group"
25+
defaultValue={ defaultValue }
26+
disabled={ disabled }
27+
multiple={ multiple }
28+
onValueChange={ onValueChange }
29+
ref={ ref }
30+
value={ value }
31+
{ ...props }>
32+
{ children }
33+
</ToggleGroup.Root>
34+
</ButtonGroupProvider>
35+
);
36+
});
37+
38+
ButtonGroup.displayName = 'ButtonGroup';
39+
40+
export {
41+
ButtonGroup,
42+
type ButtonGroupProp,
43+
};

0 commit comments

Comments
 (0)