Skip to content

feat(FR-955): move Flex component to backend.ai-ui project #3640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: feat/setup-bai-ui-pacakge
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/backendai-ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ yarn-error.log*
merged_schema.graphql

**/__generated__/*.graphql.ts

*storybook.log
21 changes: 21 additions & 0 deletions packages/backendai-ui/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
stories: [
'../src/components/**/*.mdx',
'../src/components/**/*.stories.@(js|jsx|mjs|ts|tsx)',
],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-onboarding',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/react-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
};
export default config;
14 changes: 14 additions & 0 deletions packages/backendai-ui/.storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Preview } from '@storybook/react'

const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
},
};

export default preview;
8 changes: 8 additions & 0 deletions packages/backendai-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,18 @@
"clean": "rimraf dist"
},
"peerDependencies": {
"antd": "^5.24.5",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@storybook/addon-essentials": "^8.6.12",
"@storybook/addon-interactions": "^8.6.12",
"@storybook/addon-onboarding": "^8.6.12",
"@storybook/blocks": "^8.6.12",
"@storybook/react": "^8.6.12",
"@storybook/react-vite": "^8.6.12",
"@storybook/test": "^8.6.12",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.5.0",
Expand Down
40 changes: 40 additions & 0 deletions packages/backendai-ui/src/components/Flex.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Flex, { FlexProps } from './Flex';
import type { Meta, StoryObj } from '@storybook/react';

const meta: Meta<typeof Flex> = {
title: 'Layout/Flex',
component: Flex,
parameters: {
layout: 'centered',
},
};

export default meta;

type Story = StoryObj<typeof Flex>;

const renderWithItems = ({ ...props }: FlexProps) => (
<Flex {...props}>
<button type="button">button1</button>
<button type="button">button2</button>
<button type="button">button3</button>
<button type="button">button4</button>
</Flex>
);

export const Default: Story = {
name: 'Default',
render: renderWithItems,
};

export const WithBorder: Story = {
name: 'With Border',
render: renderWithItems,
args: {
style: {
border: '1px solid #000',
width: 300,
height: 100,
},
},
};
106 changes: 106 additions & 0 deletions packages/backendai-ui/src/components/Flex.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { theme } from 'antd';
import React, { CSSProperties, PropsWithChildren } from 'react';

type GapSize = number | 'xxs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
type GapProp = GapSize | [GapSize, GapSize];

export interface FlexProps
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'dir'>,
PropsWithChildren {
direction?: 'row' | 'row-reverse' | 'column' | 'column-reverse';
wrap?: 'nowrap' | 'wrap' | 'wrap-reverse';
justify?: 'start' | 'end' | 'center' | 'between' | 'around';
align?: 'start' | 'end' | 'center' | 'baseline' | 'stretch';
gap?: GapProp;
}

const Flex = React.forwardRef<HTMLDivElement, FlexProps>(
(
{
direction = 'row',
wrap = 'nowrap',
justify = 'flex-start',
Copy link
Preview

Copilot AI May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default prop for justify is set to 'flex-start', but the FlexProps type expects one of 'start' | 'end' | 'center' | 'between' | 'around'. Change it to 'start' so the mapping logic applies correctly.

Suggested change
justify = 'flex-start',
justify = 'start',

Copilot uses AI. Check for mistakes.

align = 'center',
gap = 0,
style,
children,
...restProps
},
ref,
) => {
const { token } = theme.useToken();

const getGapSize = (size: GapSize) => {
return typeof size === 'string'
? // @ts-ignore
token['padding' + size.toUpperCase()]
: size;
Comment on lines +34 to +37
Copy link
Preview

Copilot AI May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using @ts-ignore can hide real typing issues. Consider refining the theme token type or using a type-safe lookup method instead of suppressing the error.

Suggested change
return typeof size === 'string'
? // @ts-ignore
token['padding' + size.toUpperCase()]
: size;
if (typeof size === 'string') {
const key = `padding${size.toUpperCase()}` as keyof typeof token;
return token[key] || 0; // Fallback to 0 if the key does not exist
}
return size;

Copilot uses AI. Check for mistakes.

};

const gapStyle = Array.isArray(gap)
? `${getGapSize(gap[0])}px ${getGapSize(gap[1])}px`
: getGapSize(gap);

const transferConst = [justify, align];
const transferConstStyle = transferConst.map((el) => {
let tempTxt;
switch (el) {
case 'start':
tempTxt = 'flex-start';
break;
case 'end':
tempTxt = 'flex-end';
break;
case 'between':
tempTxt = 'space-between';
break;
case 'around':
tempTxt = 'space-around';
break;
default:
tempTxt = el;
break;
}

return tempTxt;
});
const flexStyle: CSSProperties = {
display: 'flex',
flexDirection: direction,
flexWrap: wrap,
justifyContent: transferConstStyle[0],
alignItems: transferConstStyle[1],
...style,
};

return (
<div
ref={ref}
style={{
alignItems: 'stretch',
Copy link
Preview

Copilot AI May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] There are duplicate style properties between the hard-coded base object (e.g., alignItems, flexDirection, display) and the merged flexStyle. Consider consolidating these into a single style object to reduce redundancy and simplify maintenance.

Copilot uses AI. Check for mistakes.

border: '0 solid black',
boxSizing: 'border-box',
display: 'flex',
flexBasis: 'auto',
flexDirection: 'column',
flexShrink: 0,
listStyle: 'none',
margin: 0,
minHeight: 0,
minWidth: 0,
padding: 0,
position: 'relative',
textDecoration: 'none',
gap: gapStyle,
...flexStyle,
}}
{...restProps}
>
{children}
</div>
);
},
);

Flex.displayName = 'Flex';
export default Flex;
4 changes: 1 addition & 3 deletions packages/backendai-ui/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
// Export all components from this file
export { default as Button } from './components/Button';
// Add more component exports as you create them
export { default as Flex } from './components/Flex';
Loading