| @@ -17,14 +17,25 @@ initialize(); | |||||
| const preview: Preview = { | const preview: Preview = { | ||||
| parameters: { | parameters: { | ||||
| controls: { | controls: { | ||||
| expanded: true, | |||||
| sort: 'requiredFirst', | |||||
| matchers: { | matchers: { | ||||
| color: /(background|color)$/i, | color: /(background|color)$/i, | ||||
| date: /Date$/i, | date: /Date$/i, | ||||
| }, | }, | ||||
| }, | }, | ||||
| backgrounds: { | |||||
| values: [ | |||||
| { name: 'Dark', value: '#000' }, | |||||
| { name: 'Gray', value: '#f9fafb' }, | |||||
| { name: 'Light', value: '#FFF' }, | |||||
| ], | |||||
| default: 'Light', | |||||
| }, | |||||
| options: { | options: { | ||||
| storySort: { | storySort: { | ||||
| method: 'alphabetical', | method: 'alphabetical', | ||||
| order: ['Documentation', 'Components'], | |||||
| }, | }, | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -36,12 +36,14 @@ | |||||
| "start:mock": "cross-env REACT_APP_ENV=dev UMI_ENV=dev max dev", | "start:mock": "cross-env REACT_APP_ENV=dev UMI_ENV=dev max dev", | ||||
| "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev", | "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev", | ||||
| "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev", | "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev", | ||||
| "storybook": "storybook dev -p 6006", | |||||
| "storybook-build": "storybook build", | |||||
| "storybook-docs": "storybook dev --docs", | |||||
| "storybook-docs-build": "storybook build --docs", | |||||
| "test": "jest", | "test": "jest", | ||||
| "test:coverage": "npm run jest -- --coverage", | "test:coverage": "npm run jest -- --coverage", | ||||
| "test:update": "npm run jest -- -u", | "test:update": "npm run jest -- -u", | ||||
| "tsc": "tsc --noEmit", | |||||
| "storybook": "storybook dev -p 6006", | |||||
| "build-storybook": "storybook build" | |||||
| "tsc": "tsc --noEmit" | |||||
| }, | }, | ||||
| "lint-staged": { | "lint-staged": { | ||||
| "**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js", | "**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js", | ||||
| @@ -166,4 +168,4 @@ | |||||
| "public" | "public" | ||||
| ] | ] | ||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -8,15 +8,28 @@ import CodeSelectorModal from '@/components/CodeSelectorModal'; | |||||
| import KFIcon from '@/components/KFIcon'; | import KFIcon from '@/components/KFIcon'; | ||||
| import { openAntdModal } from '@/utils/modal'; | import { openAntdModal } from '@/utils/modal'; | ||||
| import { Button } from 'antd'; | import { Button } from 'antd'; | ||||
| import classNames from 'classnames'; | |||||
| import ParameterInput, { type ParameterInputProps } from '../ParameterInput'; | import ParameterInput, { type ParameterInputProps } from '../ParameterInput'; | ||||
| import './index.less'; | import './index.less'; | ||||
| export { requiredValidator, type ParameterInputObject } from '../ParameterInput'; | |||||
| export { | |||||
| requiredValidator, | |||||
| type ParameterInputObject, | |||||
| type ParameterInputValue, | |||||
| } from '../ParameterInput'; | |||||
| type CodeSelectProps = ParameterInputProps; | type CodeSelectProps = ParameterInputProps; | ||||
| /** 代码配置选择表单组件 */ | /** 代码配置选择表单组件 */ | ||||
| function CodeSelect({ value, onChange, disabled, ...rest }: CodeSelectProps) { | |||||
| function CodeSelect({ | |||||
| value, | |||||
| size, | |||||
| disabled, | |||||
| className, | |||||
| style, | |||||
| onChange, | |||||
| ...rest | |||||
| }: CodeSelectProps) { | |||||
| const selectResource = () => { | const selectResource = () => { | ||||
| const { close } = openAntdModal(CodeSelectorModal, { | const { close } = openAntdModal(CodeSelectorModal, { | ||||
| onOk: (res) => { | onOk: (res) => { | ||||
| @@ -47,9 +60,10 @@ function CodeSelect({ value, onChange, disabled, ...rest }: CodeSelectProps) { | |||||
| }; | }; | ||||
| return ( | return ( | ||||
| <div className="kf-code-select"> | |||||
| <div className={classNames('kf-code-select', className)} style={style}> | |||||
| <ParameterInput | <ParameterInput | ||||
| {...rest} | {...rest} | ||||
| size={size} | |||||
| disabled={disabled} | disabled={disabled} | ||||
| value={value} | value={value} | ||||
| onChange={onChange} | onChange={onChange} | ||||
| @@ -57,7 +71,7 @@ function CodeSelect({ value, onChange, disabled, ...rest }: CodeSelectProps) { | |||||
| ></ParameterInput> | ></ParameterInput> | ||||
| <Button | <Button | ||||
| className="kf-code-select__button" | className="kf-code-select__button" | ||||
| size="large" | |||||
| size={size} | |||||
| type="link" | type="link" | ||||
| icon={<KFIcon type="icon-xuanzedaimapeizhi" font={16} />} | icon={<KFIcon type="icon-xuanzedaimapeizhi" font={16} />} | ||||
| disabled={disabled} | disabled={disabled} | ||||
| @@ -49,18 +49,31 @@ | |||||
| padding: 10px 11px; | padding: 10px 11px; | ||||
| font-size: @font-size-input-lg; | font-size: @font-size-input-lg; | ||||
| .parameter-input__placeholder { | |||||
| .parameter-input__placeholder, | |||||
| .parameter-input__content__value { | |||||
| min-height: 24px; | |||||
| font-size: @font-size-input-lg; | font-size: @font-size-input-lg; | ||||
| line-height: 1.5; | line-height: 1.5; | ||||
| } | } | ||||
| .parameter-input__content__close-icon { | |||||
| font-size: 12px; | |||||
| } | |||||
| } | |||||
| .parameter-input.parameter-input--small { | |||||
| padding: 0 7px; | |||||
| font-size: @font-size-input; | |||||
| .parameter-input__placeholder, | |||||
| .parameter-input__content__value { | .parameter-input__content__value { | ||||
| font-size: @font-size-input-lg; | |||||
| line-height: 1.5; | |||||
| min-height: 22px; | |||||
| font-size: @font-size-input; | |||||
| line-height: 1.5714285714285714; | |||||
| } | } | ||||
| .parameter-input__content__close-icon { | .parameter-input__content__close-icon { | ||||
| font-size: 12px; | |||||
| font-size: 10px; | |||||
| } | } | ||||
| } | } | ||||
| @@ -105,6 +105,7 @@ function ParameterInput({ | |||||
| className={classNames( | className={classNames( | ||||
| 'parameter-input', | 'parameter-input', | ||||
| { 'parameter-input--large': size === 'large' }, | { 'parameter-input--large': size === 'large' }, | ||||
| { 'parameter-input--small': size === 'small' }, | |||||
| { [`parameter-input--${status}`]: status }, | { [`parameter-input--${status}`]: status }, | ||||
| className, | className, | ||||
| )} | )} | ||||
| @@ -12,12 +12,17 @@ import ResourceSelectorModal, { | |||||
| } from '@/components/ResourceSelectorModal'; | } from '@/components/ResourceSelectorModal'; | ||||
| import { openAntdModal } from '@/utils/modal'; | import { openAntdModal } from '@/utils/modal'; | ||||
| import { Button } from 'antd'; | import { Button } from 'antd'; | ||||
| import classNames from 'classnames'; | |||||
| import { pick } from 'lodash'; | import { pick } from 'lodash'; | ||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| import ParameterInput, { type ParameterInputProps } from '../ParameterInput'; | import ParameterInput, { type ParameterInputProps } from '../ParameterInput'; | ||||
| import './index.less'; | import './index.less'; | ||||
| export { requiredValidator, type ParameterInputObject } from '../ParameterInput'; | |||||
| export { | |||||
| requiredValidator, | |||||
| type ParameterInputObject, | |||||
| type ParameterInputValue, | |||||
| } from '../ParameterInput'; | |||||
| export { ResourceSelectorType, selectorTypeConfig, type ResourceSelectorResponse }; | export { ResourceSelectorType, selectorTypeConfig, type ResourceSelectorResponse }; | ||||
| interface ResourceSelectProps extends ParameterInputProps { | interface ResourceSelectProps extends ParameterInputProps { | ||||
| @@ -31,7 +36,16 @@ const getSelectBtnIcon = (type: ResourceSelectorType) => { | |||||
| }; | }; | ||||
| /** 数据集、模型、镜像选择表单组件 */ | /** 数据集、模型、镜像选择表单组件 */ | ||||
| function ResourceSelect({ type, value, onChange, disabled, ...rest }: ResourceSelectProps) { | |||||
| function ResourceSelect({ | |||||
| type, | |||||
| value, | |||||
| size, | |||||
| disabled, | |||||
| className, | |||||
| style, | |||||
| onChange, | |||||
| ...rest | |||||
| }: ResourceSelectProps) { | |||||
| const [selectedResource, setSelectedResource] = useState<ResourceSelectorResponse | undefined>( | const [selectedResource, setSelectedResource] = useState<ResourceSelectorResponse | undefined>( | ||||
| undefined, | undefined, | ||||
| ); | ); | ||||
| @@ -115,18 +129,19 @@ function ResourceSelect({ type, value, onChange, disabled, ...rest }: ResourceSe | |||||
| }; | }; | ||||
| return ( | return ( | ||||
| <div className="kf-resource-select"> | |||||
| <div className={classNames('kf-resource-select', className)} style={style}> | |||||
| <ParameterInput | <ParameterInput | ||||
| {...rest} | {...rest} | ||||
| disabled={disabled} | disabled={disabled} | ||||
| value={value} | value={value} | ||||
| size={size} | |||||
| onChange={onChange} | onChange={onChange} | ||||
| onRemove={() => setSelectedResource(undefined)} | onRemove={() => setSelectedResource(undefined)} | ||||
| onClick={selectResource} | onClick={selectResource} | ||||
| ></ParameterInput> | ></ParameterInput> | ||||
| <Button | <Button | ||||
| className="kf-resource-select__button" | className="kf-resource-select__button" | ||||
| size="large" | |||||
| size={size} | |||||
| type="link" | type="link" | ||||
| icon={getSelectBtnIcon(type)} | icon={getSelectBtnIcon(type)} | ||||
| disabled={disabled} | disabled={disabled} | ||||
| @@ -1,4 +1,5 @@ | |||||
| import CodeSelect from '@/components/CodeSelect'; | |||||
| import CodeSelect, { type ParameterInputValue } from '@/components/CodeSelect'; | |||||
| import { useArgs } from '@storybook/preview-api'; | |||||
| import type { Meta, StoryObj } from '@storybook/react'; | import type { Meta, StoryObj } from '@storybook/react'; | ||||
| import { fn } from '@storybook/test'; | import { fn } from '@storybook/test'; | ||||
| import { Col, Form, Row } from 'antd'; | import { Col, Form, Row } from 'antd'; | ||||
| @@ -35,6 +36,24 @@ type Story = StoryObj<typeof meta>; | |||||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | ||||
| export const Primary: Story = { | export const Primary: Story = { | ||||
| args: { | |||||
| canInput: false, | |||||
| textArea: false, | |||||
| size: 'large', | |||||
| style: { width: 400 }, | |||||
| }, | |||||
| render: function Render(args) { | |||||
| const [{ value }, updateArgs] = useArgs(); | |||||
| function handleChange(value?: ParameterInputValue) { | |||||
| updateArgs({ value: value }); | |||||
| args.onChange?.(value); | |||||
| } | |||||
| return <CodeSelect {...args} value={value} onChange={handleChange}></CodeSelect>; | |||||
| }, | |||||
| }; | |||||
| export const InForm: Story = { | |||||
| render: ({ onChange }) => { | render: ({ onChange }) => { | ||||
| return ( | return ( | ||||
| <Form name="code-select-form" size="large"> | <Form name="code-select-form" size="large"> | ||||
| @@ -13,7 +13,7 @@ const meta = { | |||||
| tags: ['autodocs'], | tags: ['autodocs'], | ||||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | // More on argTypes: https://storybook.js.org/docs/api/argtypes | ||||
| argTypes: { | argTypes: { | ||||
| type: { control: 'select', options: Object.values(EmptyType) }, | |||||
| type: { control: 'radio', options: Object.values(EmptyType) }, | |||||
| }, | }, | ||||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | ||||
| args: { onButtonClick: fn() }, | args: { onButtonClick: fn() }, | ||||
| @@ -1,7 +1,9 @@ | |||||
| import ResourceSelect, { | import ResourceSelect, { | ||||
| ParameterInputValue, | |||||
| requiredValidator, | requiredValidator, | ||||
| ResourceSelectorType, | ResourceSelectorType, | ||||
| } from '@/components/ResourceSelect'; | } from '@/components/ResourceSelect'; | ||||
| import { useArgs } from '@storybook/preview-api'; | |||||
| import type { Meta, StoryObj } from '@storybook/react'; | import type { Meta, StoryObj } from '@storybook/react'; | ||||
| import { fn } from '@storybook/test'; | import { fn } from '@storybook/test'; | ||||
| import { Col, Form, Row } from 'antd'; | import { Col, Form, Row } from 'antd'; | ||||
| @@ -57,7 +59,7 @@ const meta = { | |||||
| tags: ['autodocs'], | tags: ['autodocs'], | ||||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | // More on argTypes: https://storybook.js.org/docs/api/argtypes | ||||
| argTypes: { | argTypes: { | ||||
| // backgroundColor: { control: 'color' }, | |||||
| type: { control: 'radio', options: Object.values(ResourceSelectorType) }, | |||||
| }, | }, | ||||
| // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args | ||||
| args: { onChange: fn() }, | args: { onChange: fn() }, | ||||
| @@ -68,6 +70,25 @@ type Story = StoryObj<typeof meta>; | |||||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | ||||
| export const Primary: Story = { | export const Primary: Story = { | ||||
| args: { | |||||
| type: ResourceSelectorType.Dataset, | |||||
| canInput: false, | |||||
| textArea: false, | |||||
| size: 'large', | |||||
| style: { width: 400 }, | |||||
| }, | |||||
| render: function Render(args) { | |||||
| const [{ value }, updateArgs] = useArgs(); | |||||
| function handleChange(value?: ParameterInputValue) { | |||||
| updateArgs({ value: value }); | |||||
| args.onChange?.(value); | |||||
| } | |||||
| return <ResourceSelect {...args} value={value} onChange={handleChange}></ResourceSelect>; | |||||
| }, | |||||
| }; | |||||
| export const InForm: Story = { | |||||
| args: { | args: { | ||||
| type: ResourceSelectorType.Dataset, | type: ResourceSelectorType.Dataset, | ||||
| }, | }, | ||||
| @@ -82,7 +103,17 @@ export const Primary: Story = { | |||||
| > | > | ||||
| <Row gutter={8}> | <Row gutter={8}> | ||||
| <Col span={10}> | <Col span={10}> | ||||
| <Form.Item label="数据集" name="dataset"> | |||||
| <Form.Item | |||||
| label="数据集" | |||||
| name="dataset" | |||||
| rules={[ | |||||
| { | |||||
| validator: requiredValidator, | |||||
| message: '请选择数据集', | |||||
| }, | |||||
| ]} | |||||
| required | |||||
| > | |||||
| <ResourceSelect | <ResourceSelect | ||||
| type={ResourceSelectorType.Dataset} | type={ResourceSelectorType.Dataset} | ||||
| placeholder="请选择" | placeholder="请选择" | ||||
| @@ -95,17 +126,7 @@ export const Primary: Story = { | |||||
| </Row> | </Row> | ||||
| <Row gutter={8}> | <Row gutter={8}> | ||||
| <Col span={10}> | <Col span={10}> | ||||
| <Form.Item | |||||
| label="模型" | |||||
| name="model" | |||||
| rules={[ | |||||
| { | |||||
| validator: requiredValidator, | |||||
| message: '请选择镜像', | |||||
| }, | |||||
| ]} | |||||
| required | |||||
| > | |||||
| <Form.Item label="模型" name="model"> | |||||
| <ResourceSelect | <ResourceSelect | ||||
| type={ResourceSelectorType.Model} | type={ResourceSelectorType.Model} | ||||
| placeholder="请选择" | placeholder="请选择" | ||||