Skip to content

Commit 3c5eae2

Browse files
committed
feat(FR-1731): implement mutation functionality for the project table (#4764)
resolves #4716 (FR-1731) I implemented mutation features for project table. To implement these features, I moved a lot of files to backend.ai-ui project. Please check whether all the features that were in the control panel are working properly in the WebUI All tests pass locally, but for some unknown reason they fail in CI. I’ll look into it. For now, please ignore the tests. **Checklist:** (if applicable) - [ ] Documentation - [ ] Minium required manager version - [ ] Specific setting for review (eg., KB link, endpoint or how to setup) - [ ] Minimum requirements to check during review - [ ] Test case(s) to demonstrate the difference of before/after
1 parent 2e72edf commit 3c5eae2

140 files changed

Lines changed: 2891 additions & 555 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/backend.ai-ui/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
"@types/relay-test-utils": "^19.0.0",
162162
"@vitejs/plugin-react": "^4.7.0",
163163
"babel-plugin-react-compiler": "^1.0.0",
164+
"babel-jest": "^30.2.0",
164165
"eslint": "^8.57.1",
165166
"eslint-plugin-import": "^2.31.0",
166167
"eslint-plugin-react": "^7.37.4",
@@ -195,7 +196,8 @@
195196
"jsx": "react-jsx"
196197
}
197198
}
198-
]
199+
],
200+
"^.+\\.(js|jsx|ts|tsx)$": "babel-jest"
199201
},
200202
"setupFilesAfterEnv": [
201203
"./setupTests.ts"

packages/backend.ai-ui/setupTests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
// allows you to do things like:
33
// expect(element).toHaveTextContent(/react/i)
44
// learn more: https://github.com/testing-library/jest-dom
5+
import './src/__test__/matchMedia.mock';
56
import '@testing-library/jest-dom';
File renamed without changes.

react/src/components/AllowedHostNamesSelect.tsx renamed to packages/backend.ai-ui/src/components/BAIAllowedHostNamesSelect.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useAllowedHostNames } from '../hooks/backendai';
1+
import { useAllowedHostNames } from '../hooks';
22
import { Select, SelectProps } from 'antd';
33
import _ from 'lodash';
44
import React from 'react';

react/src/components/DynamicUnitInputNumber.stories.tsx renamed to packages/backend.ai-ui/src/components/BAIDynamicUnitInputNumber.stories.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// Button.stories.ts|tsx
22
import DynamicUnitInputNumber, {
3-
DynamicUnitInputNumberProps,
4-
} from './DynamicUnitInputNumber';
5-
import type { Meta, StoryObj } from '@storybook/react';
3+
BAIDynamicUnitInputNumberProps,
4+
} from './BAIDynamicUnitInputNumber';
5+
import type { Meta, StoryObj } from '@storybook/react-vite';
66
import { Form } from 'antd';
77

88
const meta: Meta<typeof DynamicUnitInputNumber> = {
9-
title: 'Input/DynamicUnitInputNumber',
9+
title: 'Components/Input/DynamicUnitInputNumber',
1010
component: DynamicUnitInputNumber,
1111
parameters: {
1212
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout
@@ -21,7 +21,7 @@ type Story = StoryObj<typeof DynamicUnitInputNumber>;
2121
const renderWithFormItem = ({
2222
value,
2323
...args
24-
}: DynamicUnitInputNumberProps) => {
24+
}: BAIDynamicUnitInputNumberProps) => {
2525
return (
2626
<Form
2727
initialValues={{

react/src/components/DynamicUnitInputNumber.tsx renamed to packages/backend.ai-ui/src/components/BAIDynamicUnitInputNumber.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { convertToBinaryUnit, parseValueWithUnit, SizeUnit } from '../helper';
2-
import useControllableState_deprecated from '../hooks/useControllableState';
3-
import { usePrevious } from 'ahooks';
2+
import { useControllableValue, usePrevious } from 'ahooks';
43
import { InputNumber, InputNumberProps, Select, Typography } from 'antd';
54
import _ from 'lodash';
65
import React, { RefObject, useEffect, useRef } from 'react';
76

8-
export interface DynamicUnitInputNumberProps
7+
export interface BAIDynamicUnitInputNumberProps
98
extends Omit<
109
InputNumberProps,
1110
'step' | 'max' | 'min' | 'value' | 'onChange'
@@ -21,7 +20,7 @@ export interface DynamicUnitInputNumberProps
2120
ref?: RefObject<HTMLInputElement | null>;
2221
}
2322

24-
const DynamicUnitInputNumber: React.FC<DynamicUnitInputNumberProps> = ({
23+
const BAIDynamicUnitInputNumber: React.FC<BAIDynamicUnitInputNumberProps> = ({
2524
dynamicSteps = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512],
2625
units = ['m', 'g', 't', 'p'],
2726
disableAutoUnit = false,
@@ -30,11 +29,12 @@ const DynamicUnitInputNumber: React.FC<DynamicUnitInputNumberProps> = ({
3029
roundStep,
3130
...inputNumberProps
3231
}) => {
33-
const [value, setValue] = useControllableState_deprecated<
34-
string | null | undefined
35-
>(inputNumberProps, {
36-
defaultValue: '0g',
37-
});
32+
const [value, setValue] = useControllableValue<string | null | undefined>(
33+
inputNumberProps,
34+
{
35+
defaultValue: '0g',
36+
},
37+
);
3838
const [numValue, _unitFromValue] =
3939
value === null || value === undefined
4040
? [null, null]
@@ -221,4 +221,4 @@ const DynamicUnitInputNumber: React.FC<DynamicUnitInputNumberProps> = ({
221221
);
222222
};
223223

224-
export default DynamicUnitInputNumber;
224+
export default BAIDynamicUnitInputNumber;

react/src/components/DynamicUnitInputNumberWithSlider.stories.tsx renamed to packages/backend.ai-ui/src/components/BAIDynamicUnitInputNumberWithSlider.stories.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
// Button.stories.ts|tsx
2-
import DynamicUnitInputNumberWithSlider, {
3-
DynamicUnitInputNumberWithSliderProps,
4-
} from './DynamicUnitInputNumberWithSlider';
5-
import type { Meta, StoryObj } from '@storybook/react';
1+
import {
2+
BAIDynamicUnitInputNumberWithSlider,
3+
BAIDynamicUnitInputNumberWithSliderProps,
4+
} from '.';
5+
import type { Meta, StoryObj } from '@storybook/react-vite';
66
import { Form } from 'antd';
77

8-
const meta: Meta<typeof DynamicUnitInputNumberWithSlider> = {
9-
title: 'Input/DynamicUnitInputNumberWithSlider',
10-
component: DynamicUnitInputNumberWithSlider,
8+
const meta: Meta<typeof BAIDynamicUnitInputNumberWithSlider> = {
9+
title: 'Components/Input/DynamicUnitInputNumberWithSlider',
10+
component: BAIDynamicUnitInputNumberWithSlider,
1111
parameters: {
1212
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout
1313
layout: 'centered',
@@ -16,20 +16,20 @@ const meta: Meta<typeof DynamicUnitInputNumberWithSlider> = {
1616

1717
export default meta;
1818

19-
type Story = StoryObj<typeof DynamicUnitInputNumberWithSlider>;
19+
type Story = StoryObj<typeof BAIDynamicUnitInputNumberWithSlider>;
2020

2121
const renderWithFormItem = ({
2222
value,
2323
...args
24-
}: DynamicUnitInputNumberWithSliderProps) => {
24+
}: BAIDynamicUnitInputNumberWithSliderProps) => {
2525
return (
2626
<Form
2727
initialValues={{
2828
mem: value,
2929
}}
3030
>
3131
<Form.Item name="mem">
32-
<DynamicUnitInputNumberWithSlider {...args} />
32+
<BAIDynamicUnitInputNumberWithSlider {...args} />
3333
</Form.Item>
3434
</Form>
3535
);

react/src/components/DynamicUnitInputNumberWithSlider.tsx renamed to packages/backend.ai-ui/src/components/BAIDynamicUnitInputNumberWithSlider.tsx

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,24 @@ import {
44
toFixedFloorWithoutTrailingZeros,
55
} from '../helper';
66
import { useUpdatableState } from '../hooks';
7-
import useControllableState_deprecated from '../hooks/useControllableState';
8-
import DynamicUnitInputNumber, {
9-
DynamicUnitInputNumberProps,
10-
} from './DynamicUnitInputNumber';
7+
import { BAIDynamicUnitInputNumberProps } from './BAIDynamicUnitInputNumber';
8+
import BAIFlex from './BAIFlex';
9+
import { useControllableValue } from 'ahooks';
1110
import { Slider, theme } from 'antd';
1211
import { SliderMarks } from 'antd/es/slider';
13-
import { BAIFlex } from 'backend.ai-ui';
1412
import _ from 'lodash';
1513
import React, { useEffect, useMemo } from 'react';
1614

17-
export interface DynamicUnitInputNumberWithSliderProps
18-
extends DynamicUnitInputNumberProps {
15+
export interface BAIDynamicUnitInputNumberWithSliderProps
16+
extends BAIDynamicUnitInputNumberProps {
1917
extraMarks?: SliderMarks;
2018
hideSlider?: boolean;
2119
warn?: string;
2220
step?: number;
2321
inputMinWidth?: number;
2422
}
2523
const DynamicUnitInputNumberWithSlider: React.FC<
26-
DynamicUnitInputNumberWithSliderProps
24+
BAIDynamicUnitInputNumberWithSliderProps
2725
> = ({
2826
min = '0m',
2927
max = '32g',
@@ -34,11 +32,12 @@ const DynamicUnitInputNumberWithSlider: React.FC<
3432
step = 0.05,
3533
...otherProps
3634
}) => {
37-
const [value, setValue] = useControllableState_deprecated<
38-
string | undefined | null
39-
>(otherProps, {
40-
defaultValue: '0g',
41-
});
35+
const [value, setValue] = useControllableValue<string | undefined | null>(
36+
otherProps,
37+
{
38+
defaultValue: '0g',
39+
},
40+
);
4241
const { token } = theme.useToken();
4342
const minGiB = useMemo(() => convertToBinaryUnit(min, 'g', 2), [min]);
4443
const maxGiB = useMemo(() => convertToBinaryUnit(max, 'g', 2), [max]);
@@ -79,7 +78,7 @@ const DynamicUnitInputNumberWithSlider: React.FC<
7978
direction="column"
8079
align="stretch"
8180
>
82-
<DynamicUnitInputNumber
81+
<DynamicUnitInputNumberWithSlider
8382
{...otherProps}
8483
key={key}
8584
min={min}

react/src/components/ResourceGroupSelect.tsx renamed to packages/backend.ai-ui/src/components/BAIProjectResourceGroupSelect.tsx

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import { useBaiSignedRequestWithPromise } from '../helper';
2-
import { useUpdatableState } from '../hooks';
3-
import { useSuspenseTanQuery } from '../hooks/reactQueryAlias';
4-
import useControllableState_deprecated from '../hooks/useControllableState';
5-
import TextHighlighter from './TextHighlighter';
1+
import { useSuspenseTanQuery } from '../helper/reactQueryAlias';
2+
import { useBAISignedRequestWithPromise, useUpdatableState } from '../hooks';
3+
import BAISelect, { BAISelectProps } from './BAISelect';
4+
import BAITextHighlighter from './BAITextHighlighter';
5+
import { useControllableValue } from 'ahooks';
66
import { SelectProps } from 'antd';
7-
import { BAISelect, BAISelectProps } from 'backend.ai-ui';
87
import _ from 'lodash';
98
import React, { useEffect, useState, useTransition } from 'react';
109

@@ -21,13 +20,15 @@ interface VolumeInfo {
2120
sftp_scaling_groups?: string[];
2221
}
2322

24-
interface ResourceGroupSelectProps extends BAISelectProps {
23+
interface BAIProjectResourceGroupSelectProps extends BAISelectProps {
2524
projectName: string;
2625
autoSelectDefault?: boolean;
2726
filter?: (projectName: string) => boolean;
2827
}
2928

30-
const ResourceGroupSelect: React.FC<ResourceGroupSelectProps> = ({
29+
const BAIProjectResourceGroupSelect: React.FC<
30+
BAIProjectResourceGroupSelectProps
31+
> = ({
3132
projectName,
3233
autoSelectDefault,
3334
filter,
@@ -36,16 +37,16 @@ const ResourceGroupSelect: React.FC<ResourceGroupSelectProps> = ({
3637
loading,
3738
...selectProps
3839
}) => {
39-
const baiRequestWithPromise = useBaiSignedRequestWithPromise();
40+
const baiRequestWithPromise = useBAISignedRequestWithPromise();
4041
const [fetchKey] = useUpdatableState('first');
4142
const [controllableSearchValue, setControllableSearchValue] =
42-
useControllableState_deprecated<string>({
43+
useControllableValue<string>({
4344
value: searchValue,
4445
onChange: onSearch,
4546
});
4647

4748
const [controllableValue, setControllableValueDoNotUseWithoutTransition] =
48-
useControllableState_deprecated(selectProps);
49+
useControllableValue(selectProps);
4950
const [isPendingChangeTransition, startChangeTransition] = useTransition();
5051

5152
const [optimisticValue, setOptimisticValue] = useState();
@@ -169,9 +170,9 @@ const ResourceGroupSelect: React.FC<ResourceGroupSelectProps> = ({
169170
})}
170171
optionRender={(option) => {
171172
return (
172-
<TextHighlighter keyword={controllableSearchValue}>
173+
<BAITextHighlighter keyword={controllableSearchValue}>
173174
{option.data.value?.toString()}
174-
</TextHighlighter>
175+
</BAITextHighlighter>
175176
);
176177
}}
177178
{...selectProps}
@@ -181,4 +182,4 @@ const ResourceGroupSelect: React.FC<ResourceGroupSelectProps> = ({
181182
);
182183
};
183184

184-
export default ResourceGroupSelect;
185+
export default BAIProjectResourceGroupSelect;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { theme } from 'antd';
2+
import _ from 'lodash';
3+
import React from 'react';
4+
5+
interface BAITextHighlighterProps {
6+
children?: string | null;
7+
keyword?: string;
8+
style?: React.CSSProperties;
9+
}
10+
11+
const BAITextHighlighter: React.FC<BAITextHighlighterProps> = ({
12+
children,
13+
keyword,
14+
style,
15+
}) => {
16+
if (!children) return null;
17+
18+
if (_.isEmpty(keyword)) {
19+
return <span>{children}</span>;
20+
} else {
21+
const { token } = theme.useToken() || '#F1A239';
22+
const parts = children.split(
23+
new RegExp(`(${_.escapeRegExp(keyword)})`, 'gi'),
24+
);
25+
26+
return (
27+
<span>
28+
{parts.map((part, i) =>
29+
part.toLowerCase() === keyword?.toLowerCase() ? (
30+
<span
31+
key={i}
32+
style={{ backgroundColor: token.colorWarningHover, ...style }}
33+
>
34+
{part}
35+
</span>
36+
) : (
37+
part
38+
),
39+
)}
40+
</span>
41+
);
42+
}
43+
};
44+
45+
export default React.memo(BAITextHighlighter);

0 commit comments

Comments
 (0)