antd#TimePicker TypeScript Examples
The following examples show how to use
antd#TimePicker.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.tsx From antdp with MIT License | 6 votes |
getItem = ({ attr, type, inputNode }: {
attr?: Partial<ItemChildAttr<any, any>>;
type?: ItemChildType;
inputNode?: ((...arg: any[]) => React.ReactNode) | React.ReactNode;
}) => {
let renderItem = undefined;
if (type === 'Input') {
const inputAttr = attr as InputProps;
renderItem = <Input {...inputAttr} />;
} else if (type === 'TextArea') {
const inputAttr = attr as TextAreaProps;
renderItem = <Input.TextArea {...inputAttr} />;
} else if (type === 'InputNumber') {
const inputAttr = attr as InputNumberProps;
renderItem = <InputNumber {...inputAttr} />;
} else if (type === 'AutoComplete') {
const inputAttr = attr as AutoCompleteProps;
renderItem = <AutoComplete {...inputAttr} />;
} else if (type === 'Cascader') {
const inputAttr = attr as CascaderProps;
renderItem = <Cascader {...inputAttr} />;
} else if (type === 'DatePicker') {
const inputAttr = attr as DatePickerProps;
renderItem = <DatePicker {...inputAttr} />;
} else if (type === 'Rate') {
const inputAttr = attr as RateProps;
renderItem = <Rate {...inputAttr} />;
} else if (type === 'Slider') {
const inputAttr = attr as SliderSingleProps;
renderItem = <Slider {...inputAttr} />;
} else if (type === 'TreeSelect') {
const inputAttr = attr as TreeSelectProps<any>;
renderItem = <TreeSelect {...inputAttr} />;
} else if (type === 'Select') {
const inputAttr = attr as SelectProps<any>;
renderItem = <Select {...inputAttr} />;
} else if (type === 'Checkbox') {
const inputAttr = attr as CheckboxGroupProps;
renderItem = <Checkbox.Group {...inputAttr} />;
} else if (type === 'Mentions') {
const inputAttr = attr as MentionProps;
renderItem = <Mentions {...inputAttr} />;
} else if (type === 'Radio') {
const inputAttr = attr as RadioProps;
renderItem = <Radio.Group {...inputAttr} />;
} else if (type === 'Switch') {
const inputAttr = attr as SwitchProps;
renderItem = <Switch {...inputAttr} />;
} else if (type === 'TimePicker') {
const inputAttr = attr as TimePickerProps;
renderItem = <TimePicker {...inputAttr} />;
} else if (type === 'Upload') {
const inputAttr = attr as UploadProps;
renderItem = <Upload {...inputAttr} />;
} else if (type === 'RangePicker') {
const inputAttr = attr as RangePickerProps;
renderItem = <RangePicker {...inputAttr} />;
} else if (type === 'Custom') {
renderItem = inputNode;
}
return renderItem;
}
Example #2
Source File: GeneralTimePicker.tsx From next-basics with GNU General Public License v3.0 | 6 votes |
export function GeneralTimePicker(
props: GeneralTimePickerProps
): React.ReactElement {
const format = props.configProps?.format ?? "HH:mm:ss";
const handleChange = (time: moment.Moment, timeString: string): void => {
props.onChange?.(timeString);
};
const handleOpenChange = (flag: boolean): void => {
props.onOpenChange?.(flag, props.value);
};
return (
<FormItemWrapper {...props}>
<TimePicker
{...props.configProps}
value={
props.name && props.formElement
? undefined
: props.value && moment(props.value, format)
}
onChange={handleChange}
onOpenChange={handleOpenChange}
/>
</FormItemWrapper>
);
}
Example #3
Source File: TimeRangePicker.spec.tsx From next-basics with GNU General Public License v3.0 | 6 votes |
describe("RefTimeRangePicker", () => {
it("should work", () => {
const changeFn = jest.fn();
const wrapper = mount(
<RefTimeRangePicker
format="HH:mm:ss"
onChange={changeFn}
emitChangeOnInit={true}
/>
);
expect(wrapper.find(TimePicker)).toHaveLength(2);
expect(changeFn).toBeCalled();
let startTimePicker = wrapper.find(TimePicker).first();
startTimePicker.invoke("onChange")(
moment("00:24:00", "HH:mm:ss"),
// eslint-disable-next-line
// @ts-ignore
"00:24:00"
);
startTimePicker = wrapper.find(TimePicker).first();
let endTimePicker = wrapper.find(TimePicker).last();
endTimePicker.invoke("onChange")(
moment("23:59:00", "HH:mm:ss"),
// eslint-disable-next-line
// @ts-ignore
"23:59:00"
);
endTimePicker = wrapper.find(TimePicker).last();
expect(endTimePicker.prop("value")).toBeUndefined();
wrapper.setProps({ value: { startTime: "01:23:45", endTime: "23:59:59" } });
});
});
Example #4
Source File: GeneralTimePicker.spec.tsx From next-basics with GNU General Public License v3.0 | 5 votes |
describe("GeneralTimePicker", () => {
let clock: InstalledClock;
beforeEach(() => {
clock = install({ now: +new Date("2019-10-17 17:20:00") });
});
afterEach(() => {
clock.uninstall();
});
it("should work", async () => {
const handleChange = jest.fn();
const handleOpenChange = jest.fn();
const wrapper = shallow(
<GeneralTimePicker
name="time"
label="hello"
placeholder="when"
value="09:14:30"
onChange={handleChange}
onOpenChange={handleOpenChange}
/>
);
wrapper.find(TimePicker).invoke("onChange")(
moment("18:01:00", "HH:mm:ss"),
"18:01:00"
);
await Promise.resolve();
expect(handleChange).toBeCalledWith("18:01:00");
wrapper.setProps({ value: "18:01:00" });
wrapper.find(TimePicker).invoke("onOpenChange")(true);
expect(handleOpenChange).toBeCalledWith(true, "18:01:00");
});
it("should update value", () => {
const wrapper = mount(<GeneralTimePicker value="09:14:30" />);
expect(
wrapper
.find(TimePicker)
.prop("value")
.format("HH:mm:ss")
).toBe("09:14:30");
wrapper.setProps({
value: "18:01:00"
});
wrapper.update();
expect(
wrapper
.find(TimePicker)
.prop("value")
.format("HH:mm:ss")
).toBe("18:01:00");
});
});
Example #5
Source File: BrickTimePicker.tsx From next-basics with GNU General Public License v3.0 | 5 votes |
export function LegacyBrickTimePicker({
configProps,
onChange
}: LegacyBrickTimePickerProps): React.ReactElement {
return (
<TimePicker {...configProps} onChange={e => handleChange(e, onChange)} />
);
}
Example #6
Source File: index.tsx From metaplex with Apache License 2.0 | 5 votes |
DateTimePicker = (props: IDateTimePicker) => {
const {
momentObj,
setMomentObj,
datePickerProps = {},
timePickerProps = {},
} = props;
return (
<>
<DatePicker
className="field-date"
size="large"
value={momentObj}
onChange={value => {
if (!value) return;
if (!momentObj) return setMomentObj(value);
const currentMoment = momentObj.clone();
currentMoment.year(value.year());
currentMoment.month(value.month());
currentMoment.date(value.date());
setMomentObj(currentMoment);
}}
{...datePickerProps}
/>
<TimePicker
className="field-date"
size="large"
value={momentObj}
onChange={value => {
if (!value) return;
if (!momentObj) return setMomentObj(value);
const currentMoment = momentObj.clone();
currentMoment.hour(value.hour());
currentMoment.minute(value.minute());
currentMoment.second(value.second());
setMomentObj(currentMoment);
}}
{...timePickerProps}
/>
</>
);
}
Example #7
Source File: palette.tsx From jmix-frontend with Apache License 2.0 | 4 votes |
palette = () => (
<Palette>
<Category name="Text">
<Component name="Formatted Message">
<Variant>
<FormattedMessage />
</Variant>
</Component>
<Component name="Heading">
<Variant name="h1">
<Typography.Title></Typography.Title>
</Variant>
<Variant name="h2">
<Typography.Title level={2}></Typography.Title>
</Variant>
<Variant name="h3">
<Typography.Title level={3}></Typography.Title>
</Variant>
<Variant name="h4">
<Typography.Title level={4}></Typography.Title>
</Variant>
<Variant name="h5">
<Typography.Title level={5}></Typography.Title>
</Variant>
</Component>
<Component name="Text">
<Variant>
<Typography.Text></Typography.Text>
</Variant>
<Variant name="Secondary">
<Typography.Text type="secondary"></Typography.Text>
</Variant>
<Variant name="Success">
<Typography.Text type="success"></Typography.Text>
</Variant>
<Variant name="Warning">
<Typography.Text type="warning"></Typography.Text>
</Variant>
<Variant name="Danger">
<Typography.Text type="danger"></Typography.Text>
</Variant>
<Variant name="Disabled">
<Typography.Text disabled></Typography.Text>
</Variant>
</Component>
</Category>
<Category name="Layout">
<Component name="Divider">
<Variant>
<Divider />
</Variant>
</Component>
<Component name="Grid">
<Variant name="Simple Row">
<Row></Row>
</Variant>
<Variant name="Two columns">
<Row>
<Col span={12}></Col>
<Col span={12}></Col>
</Row>
</Variant>
<Variant name="Three columns">
<Row>
<Col span={8}></Col>
<Col span={8}></Col>
<Col span={8}></Col>
</Row>
</Variant>
</Component>
<Component name="Space">
<Variant>
<Space />
</Variant>
<Variant name="Small">
<Space size={"small"} />
</Variant>
<Variant name="Large">
<Space size={"large"} />
</Variant>
</Component>
</Category>
<Category name="Controls">
<Component name="Autocomplete">
<Variant>
<AutoComplete placeholder="input here" />
</Variant>
</Component>
<Component name="Button">
<Variant>
<Button></Button>
</Variant>
<Variant name="Primary">
<Button type="primary"></Button>
</Variant>
<Variant name="Link">
<Button type="link"></Button>
</Variant>
<Variant name="Dropdown">
<Dropdown
trigger={["click"]}
overlay={
<Menu>
<Menu.Item></Menu.Item>
<Menu.Item></Menu.Item>
<Menu.Item></Menu.Item>
</Menu>
}
>
<Button></Button>
</Dropdown>
</Variant>
</Component>
<Component name="Checkbox">
<Variant>
<Checkbox />
</Variant>
</Component>
<Component name="Switch">
<Variant>
<Switch />
</Variant>
</Component>
<Component name="Radio Group">
<Variant>
<Radio.Group>
<Radio value={1}>A</Radio>
<Radio value={2}>B</Radio>
<Radio value={3}>C</Radio>
<Radio value={4}>D</Radio>
</Radio.Group>
</Variant>
<Variant name="Button">
<Radio.Group>
<Radio.Button value={1}>A</Radio.Button>
<Radio.Button value={2}>B</Radio.Button>
<Radio.Button value={3}>C</Radio.Button>
<Radio.Button value={4}>D</Radio.Button>
</Radio.Group>
</Variant>
</Component>
<Component name="DatePicker">
<Variant>
<DatePicker />
</Variant>
<Variant name="Range">
<DatePicker.RangePicker />
</Variant>
</Component>
<Component name="TimePicker">
<Variant>
<TimePicker />
</Variant>
<Variant name="Range">
<TimePicker.RangePicker />
</Variant>
</Component>
<Component name="Input">
<Variant>
<Input />
</Variant>
<Variant name="Number">
<InputNumber />
</Variant>
</Component>
<Component name="Select">
<Variant>
<Select defaultValue="1">
<Select.Option value="1">1</Select.Option>
<Select.Option value="2">2</Select.Option>
</Select>
</Variant>
<Variant name="Multiple">
<Select defaultValue={["1"]} mode="multiple" allowClear>
<Select.Option value="1">1</Select.Option>
<Select.Option value="2">2</Select.Option>
</Select>
</Variant>
</Component>
<Component name="Link">
<Variant>
<Typography.Link href="" target="_blank"></Typography.Link>
</Variant>
</Component>
<Component name="Slider">
<Variant>
<Slider defaultValue={30} />
</Variant>
<Variant name="Range">
<Slider range defaultValue={[20, 50]} />
</Variant>
</Component>
</Category>
<Category name="Data Display">
<Component name="Field">
<Variant>
<Field
entityName={ENTITY_NAME}
disabled={readOnlyMode}
propertyName=""
formItemProps={{
style: { marginBottom: "12px" }
}}
/>
</Variant>
</Component>
<Component name="Card">
<Variant>
<Card />
</Variant>
<Variant name="With Title">
<Card>
<Card title="Card title">
<p>Card content</p>
</Card>
</Card>
</Variant>
<Variant name="My custom card">
<Card>
<Card title="Card title">
<p>Card content</p>
<Avatar />
</Card>
</Card>
</Variant>
</Component>
<Component name="Tabs">
<Variant>
<Tabs defaultActiveKey="1">
<Tabs.TabPane tab="Tab 1" key="1">
Content of Tab Pane 1
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 2" key="2">
Content of Tab Pane 2
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 3" key="3">
Content of Tab Pane 3
</Tabs.TabPane>
</Tabs>
</Variant>
<Variant name="Tab Pane">
<Tabs.TabPane></Tabs.TabPane>
</Variant>
</Component>
<Component name="Collapse">
<Variant>
<Collapse defaultActiveKey="1">
<Collapse.Panel
header="This is panel header 1"
key="1"
></Collapse.Panel>
<Collapse.Panel
header="This is panel header 2"
key="2"
></Collapse.Panel>
<Collapse.Panel
header="This is panel header 3"
key="3"
></Collapse.Panel>
</Collapse>
</Variant>
</Component>
<Component name="Image">
<Variant>
<Image width={200} src="" />
</Variant>
</Component>
<Component name="Avatar">
<Variant>
<Avatar icon={<UserOutlined />} />
</Variant>
<Variant name="Image">
<Avatar src="https://joeschmoe.io/api/v1/random" />
</Variant>
</Component>
<Component name="Badge">
<Variant>
<Badge count={1}></Badge>
</Variant>
</Component>
<Component name="Statistic">
<Variant>
<Statistic title="Title" value={112893} />
</Variant>
</Component>
<Component name="Alert">
<Variant name="Success">
<Alert message="Text" type="success" />
</Variant>
<Variant name="Info">
<Alert message="Text" type="info" />
</Variant>
<Variant name="Warning">
<Alert message="Text" type="warning" />
</Variant>
<Variant name="Error">
<Alert message="Text" type="error" />
</Variant>
</Component>
<Component name="List">
<Variant>
<List
bordered
dataSource={[]}
renderItem={item => <List.Item></List.Item>}
/>
</Variant>
</Component>
</Category>
<Category name="Icons">
<Component name="Arrow">
<Variant name="Up">
<ArrowUpOutlined />
</Variant>
<Variant name="Down">
<ArrowDownOutlined />
</Variant>
<Variant name="Left">
<ArrowLeftOutlined />
</Variant>
<Variant name="Right">
<ArrowRightOutlined />
</Variant>
</Component>
<Component name="Question">
<Variant>
<QuestionOutlined />
</Variant>
<Variant name="Circle">
<QuestionCircleOutlined />
</Variant>
</Component>
<Component name="Plus">
<Variant>
<PlusOutlined />
</Variant>
<Variant name="Circle">
<PlusCircleOutlined />
</Variant>
</Component>
<Component name="Info">
<Variant>
<InfoOutlined />
</Variant>
<Variant name="Circle">
<InfoCircleOutlined />
</Variant>
</Component>
<Component name="Exclamation">
<Variant>
<ExclamationOutlined />
</Variant>
<Variant name="Circle">
<ExclamationCircleOutlined />
</Variant>
</Component>
<Component name="Close">
<Variant>
<CloseOutlined />
</Variant>
<Variant name="Circle">
<CloseCircleOutlined />
</Variant>
</Component>
<Component name="Check">
<Variant>
<CheckOutlined />
</Variant>
<Variant name="Circle">
<CheckCircleOutlined />
</Variant>
</Component>
<Component name="Edit">
<Variant>
<EditOutlined />
</Variant>
</Component>
<Component name="Copy">
<Variant>
<CopyOutlined />
</Variant>
</Component>
<Component name="Delete">
<Variant>
<DeleteOutlined />
</Variant>
</Component>
<Component name="Bars">
<Variant>
<BarsOutlined />
</Variant>
</Component>
<Component name="Bell">
<Variant>
<BellOutlined />
</Variant>
</Component>
<Component name="Clear">
<Variant>
<ClearOutlined />
</Variant>
</Component>
<Component name="Download">
<Variant>
<DownloadOutlined />
</Variant>
</Component>
<Component name="Upload">
<Variant>
<UploadOutlined />
</Variant>
</Component>
<Component name="Sync">
<Variant>
<SyncOutlined />
</Variant>
</Component>
<Component name="Save">
<Variant>
<SaveOutlined />
</Variant>
</Component>
<Component name="Search">
<Variant>
<SearchOutlined />
</Variant>
</Component>
<Component name="Settings">
<Variant>
<SettingOutlined />
</Variant>
</Component>
<Component name="Paperclip">
<Variant>
<PaperClipOutlined />
</Variant>
</Component>
<Component name="Phone">
<Variant>
<PhoneOutlined />
</Variant>
</Component>
<Component name="Mail">
<Variant>
<MailOutlined />
</Variant>
</Component>
<Component name="Home">
<Variant>
<HomeOutlined />
</Variant>
</Component>
<Component name="Contacts">
<Variant>
<ContactsOutlined />
</Variant>
</Component>
<Component name="User">
<Variant>
<UserOutlined />
</Variant>
<Variant name="Add">
<UserAddOutlined />
</Variant>
<Variant name="Remove">
<UserDeleteOutlined />
</Variant>
</Component>
<Component name="Team">
<Variant>
<TeamOutlined />
</Variant>
</Component>
</Category>
<Category name="Screens">
<Component name="ExampleCustomScreen">
<Variant>
<ExampleCustomScreen />
</Variant>
</Component>
<Component name="CustomEntityFilterTest">
<Variant>
<CustomEntityFilterTest />
</Variant>
</Component>
<Component name="CustomFormControls">
<Variant>
<CustomFormControls />
</Variant>
</Component>
<Component name="CustomDataDisplayComponents">
<Variant>
<CustomDataDisplayComponents />
</Variant>
</Component>
<Component name="CustomAppLayouts">
<Variant>
<CustomAppLayouts />
</Variant>
</Component>
<Component name="CustomControls">
<Variant>
<CustomControls />
</Variant>
</Component>
<Component name="ErrorBoundaryTests">
<Variant>
<ErrorBoundaryTests />
</Variant>
</Component>
<Component name="TestBlankScreen">
<Variant>
<TestBlankScreen />
</Variant>
</Component>
<Component name="CarEditor">
<Variant>
<CarEditor />
</Variant>
</Component>
<Component name="CarBrowserCards">
<Variant>
<CarBrowserCards />
</Variant>
</Component>
<Component name="CarBrowserList">
<Variant>
<CarBrowserList />
</Variant>
</Component>
<Component name="CarBrowserTable">
<Variant>
<CarBrowserTable />
</Variant>
</Component>
<Component name="CarCardsGrid">
<Variant>
<CarCardsGrid />
</Variant>
</Component>
<Component name="FavoriteCars">
<Variant>
<FavoriteCars />
</Variant>
</Component>
<Component name="CarCardsWithDetails">
<Variant>
<CarCardsWithDetails />
</Variant>
</Component>
<Component name="CarTableWithFilters">
<Variant>
<CarTableWithFilters />
</Variant>
</Component>
<Component name="CarMasterDetail">
<Variant>
<CarMasterDetail />
</Variant>
</Component>
<Component name="FormWizardCompositionO2O">
<Variant>
<FormWizardCompositionO2O />
</Variant>
</Component>
<Component name="FormWizardEditor">
<Variant>
<FormWizardEditor />
</Variant>
</Component>
<Component name="FormWizardBrowserTable">
<Variant>
<FormWizardBrowserTable />
</Variant>
</Component>
<Component name="CarMultiSelectionTable">
<Variant>
<CarMultiSelectionTable />
</Variant>
</Component>
<Component name="DatatypesTestEditor">
<Variant>
<DatatypesTestEditor />
</Variant>
</Component>
<Component name="DatatypesTestBrowserCards">
<Variant>
<DatatypesTestBrowserCards />
</Variant>
</Component>
<Component name="DatatypesTestBrowserList">
<Variant>
<DatatypesTestBrowserList />
</Variant>
</Component>
<Component name="DatatypesTestBrowserTable">
<Variant>
<DatatypesTestBrowserTable />
</Variant>
</Component>
<Component name="DatatypesTestCards">
<Variant>
<DatatypesTestCards />
</Variant>
</Component>
<Component name="AssociationO2OEditor">
<Variant>
<AssociationO2OEditor />
</Variant>
</Component>
<Component name="AssociationO2OBrowserTable">
<Variant>
<AssociationO2OBrowserTable />
</Variant>
</Component>
<Component name="AssociationO2MEditor">
<Variant>
<AssociationO2MEditor />
</Variant>
</Component>
<Component name="AssociationO2MBrowserTable">
<Variant>
<AssociationO2MBrowserTable />
</Variant>
</Component>
<Component name="AssociationM2OEditor">
<Variant>
<AssociationM2OEditor />
</Variant>
</Component>
<Component name="AssociationM2OBrowserTable">
<Variant>
<AssociationM2OBrowserTable />
</Variant>
</Component>
<Component name="AssociationM2MEditor">
<Variant>
<AssociationM2MEditor />
</Variant>
</Component>
<Component name="AssociationM2MBrowserTable">
<Variant>
<AssociationM2MBrowserTable />
</Variant>
</Component>
<Component name="CompositionO2OEditor">
<Variant>
<CompositionO2OEditor />
</Variant>
</Component>
<Component name="CompositionO2OBrowserTable">
<Variant>
<CompositionO2OBrowserTable />
</Variant>
</Component>
<Component name="CompositionO2MEditor">
<Variant>
<CompositionO2MEditor />
</Variant>
</Component>
<Component name="CompositionO2MBrowserTable">
<Variant>
<CompositionO2MBrowserTable />
</Variant>
</Component>
<Component name="DeeplyNestedTestEntityEditor">
<Variant>
<DeeplyNestedTestEntityEditor />
</Variant>
</Component>
<Component name="DeeplyNestedO2MTestEntityTable">
<Variant>
<DeeplyNestedO2MTestEntityTable />
</Variant>
</Component>
<Component name="DeeplyNestedO2MTestEntityEditor">
<Variant>
<DeeplyNestedO2MTestEntityEditor />
</Variant>
</Component>
<Component name="IntIdEditor">
<Variant>
<IntIdEditor />
</Variant>
</Component>
<Component name="IntIdBrowserTable">
<Variant>
<IntIdBrowserTable />
</Variant>
</Component>
<Component name="IntIdBrowserCards">
<Variant>
<IntIdBrowserCards />
</Variant>
</Component>
<Component name="IntIdBrowserList">
<Variant>
<IntIdBrowserList />
</Variant>
</Component>
<Component name="IntIdentityIdCards">
<Variant>
<IntIdentityIdCards />
</Variant>
</Component>
<Component name="IntIdentityIdEditor">
<Variant>
<IntIdentityIdEditor />
</Variant>
</Component>
<Component name="IntIdentityIdBrowserTable">
<Variant>
<IntIdentityIdBrowserTable />
</Variant>
</Component>
<Component name="IntIdentityIdBrowserCards">
<Variant>
<IntIdentityIdBrowserCards />
</Variant>
</Component>
<Component name="IntIdentityIdBrowserList">
<Variant>
<IntIdentityIdBrowserList />
</Variant>
</Component>
<Component name="StringIdCards">
<Variant>
<StringIdCards />
</Variant>
</Component>
<Component name="StringIdMgtCardsEdit">
<Variant>
<StringIdMgtCardsEdit />
</Variant>
</Component>
<Component name="StringIdBrowserCards">
<Variant>
<StringIdBrowserCards />
</Variant>
</Component>
<Component name="StringIdBrowserList">
<Variant>
<StringIdBrowserList />
</Variant>
</Component>
<Component name="StringIdBrowserTable">
<Variant>
<StringIdBrowserTable />
</Variant>
</Component>
<Component name="WeirdStringIdEditor">
<Variant>
<WeirdStringIdEditor />
</Variant>
</Component>
<Component name="WeirdStringIdBrowserCards">
<Variant>
<WeirdStringIdBrowserCards />
</Variant>
</Component>
<Component name="WeirdStringIdBrowserList">
<Variant>
<WeirdStringIdBrowserList />
</Variant>
</Component>
<Component name="WeirdStringIdBrowserTable">
<Variant>
<WeirdStringIdBrowserTable />
</Variant>
</Component>
<Component name="BoringStringIdEditor">
<Variant>
<BoringStringIdEditor />
</Variant>
</Component>
<Component name="BoringStringIdBrowserTable">
<Variant>
<BoringStringIdBrowserTable />
</Variant>
</Component>
<Component name="TrickyIdEditor">
<Variant>
<TrickyIdEditor />
</Variant>
</Component>
<Component name="TrickyIdBrowserTable">
<Variant>
<TrickyIdBrowserTable />
</Variant>
</Component>
</Category>
</Palette>
)
Example #8
Source File: palette.tsx From jmix-frontend with Apache License 2.0 | 4 votes |
palette = () =>
<Palette>
<Category name="Text">
<Component name="Formatted Message">
<Variant>
<FormattedMessage />
</Variant>
</Component>
<Component name="Heading">
<Variant name='h1'>
<Typography.Title></Typography.Title>
</Variant>
<Variant name='h2'>
<Typography.Title level = {2}></Typography.Title>
</Variant>
<Variant name='h3'>
<Typography.Title level = {3}></Typography.Title>
</Variant>
<Variant name='h4'>
<Typography.Title level = {4}></Typography.Title>
</Variant>
<Variant name='h5'>
<Typography.Title level = {5}></Typography.Title>
</Variant>
</Component>
<Component name='Text'>
<Variant>
<Typography.Text></Typography.Text>
</Variant>
<Variant name = 'Secondary'>
<Typography.Text type="secondary"></Typography.Text>
</Variant>
<Variant name = 'Success'>
<Typography.Text type="success"></Typography.Text>
</Variant>
<Variant name = 'Warning'>
<Typography.Text type="warning"></Typography.Text>
</Variant>
<Variant name = 'Danger'>
<Typography.Text type="danger"></Typography.Text>
</Variant>
<Variant name = 'Disabled'>
<Typography.Text disabled></Typography.Text>
</Variant>
</Component>
</Category>
<Category name="Layout">
<Component name="Divider">
<Variant>
<Divider />
</Variant>
</Component>
<Component name="Grid">
<Variant name="Simple Row">
<Row></Row>
</Variant>
<Variant name="Two columns">
<Row>
<Col span={12}></Col>
<Col span={12}></Col>
</Row>
</Variant>
<Variant name="Three columns">
<Row>
<Col span={8}></Col>
<Col span={8}></Col>
<Col span={8}></Col>
</Row>
</Variant>
</Component>
<Component name="Space">
<Variant>
<Space />
</Variant>
<Variant name="Small">
<Space size={"small"} />
</Variant>
<Variant name="Large">
<Space size={"large"} />
</Variant>
</Component>
</Category>
<Category name="Controls">
<Component name="Autocomplete">
<Variant>
<AutoComplete placeholder="input here" />
</Variant>
</Component>
<Component name="Button">
<Variant>
<Button></Button>
</Variant>
<Variant name="Primary">
<Button type="primary" ></Button>
</Variant>
<Variant name="Link">
<Button type="link" ></Button>
</Variant>
<Variant name="Dropdown">
<Dropdown
trigger={['click']}
overlay={<Menu>
<Menu.Item>
</Menu.Item>
<Menu.Item>
</Menu.Item>
<Menu.Item>
</Menu.Item>
</Menu>}
>
<Button></Button>
</Dropdown>
</Variant>
</Component>
<Component name="Checkbox">
<Variant>
<Checkbox />
</Variant>
</Component>
<Component name='Switch'>
<Variant>
<Switch />
</Variant>
</Component>
<Component name='Radio Group'>
<Variant>
<Radio.Group>
<Radio value={1}>A</Radio>
<Radio value={2}>B</Radio>
<Radio value={3}>C</Radio>
<Radio value={4}>D</Radio>
</Radio.Group>
</Variant>
<Variant name = 'Button'>
<Radio.Group>
<Radio.Button value={1}>A</Radio.Button>
<Radio.Button value={2}>B</Radio.Button>
<Radio.Button value={3}>C</Radio.Button>
<Radio.Button value={4}>D</Radio.Button>
</Radio.Group>
</Variant>
</Component>
<Component name="DatePicker">
<Variant>
<DatePicker />
</Variant>
<Variant name="Range">
<DatePicker.RangePicker />
</Variant>
</Component>
<Component name="TimePicker">
<Variant>
<TimePicker />
</Variant>
<Variant name="Range">
<TimePicker.RangePicker />
</Variant>
</Component>
<Component name="Input">
<Variant>
<Input />
</Variant>
<Variant name='Number'>
<InputNumber />
</Variant>
</Component>
<Component name='Select'>
<Variant>
<Select defaultValue="1">
<Select.Option value="1">1</Select.Option>
<Select.Option value="2">2</Select.Option>
</Select>
</Variant>
<Variant name='Multiple'>
<Select
defaultValue={["1"]}
mode="multiple"
allowClear
>
<Select.Option value="1">1</Select.Option>
<Select.Option value="2">2</Select.Option>
</Select>
</Variant>
</Component>
<Component name="Link">
<Variant>
<Typography.Link href="" target="_blank">
</Typography.Link>
</Variant>
</Component>
<Component name='Slider'>
<Variant>
<Slider defaultValue={30} />
</Variant>
<Variant name = 'Range'>
<Slider range defaultValue={[20, 50]}/>
</Variant>
</Component>
</Category>
<Category name="Data Display">
<Component name="Field">
<Variant>
<Field
entityName={ENTITY_NAME}
disabled={readOnlyMode}
propertyName=''
formItemProps={{
style: { marginBottom: "12px" }
}}
/>
</Variant>
</Component>
<Component name="Card">
<Variant>
<Card />
</Variant>
<Variant name="With Title">
<Card>
<Card title="Card title">
<p>Card content</p>
</Card>
</Card>
</Variant>
<Variant name="My custom card">
<Card>
<Card title="Card title">
<p>Card content</p>
<Avatar />
</Card>
</Card>
</Variant>
</Component>
<Component name="Tabs">
<Variant>
<Tabs defaultActiveKey="1">
<Tabs.TabPane tab="Tab 1" key="1">
Content of Tab Pane 1
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 2" key="2">
Content of Tab Pane 2
</Tabs.TabPane>
<Tabs.TabPane tab="Tab 3" key="3">
Content of Tab Pane 3
</Tabs.TabPane>
</Tabs>
</Variant>
<Variant name = "Tab Pane">
<Tabs.TabPane>
</Tabs.TabPane>
</Variant>
</Component>
<Component name="Collapse">
<Variant>
<Collapse defaultActiveKey='1'>
<Collapse.Panel header="This is panel header 1" key="1">
</Collapse.Panel>
<Collapse.Panel header="This is panel header 2" key="2">
</Collapse.Panel>
<Collapse.Panel header="This is panel header 3" key="3">
</Collapse.Panel>
</Collapse>
</Variant>
</Component>
<Component name="Image">
<Variant>
<Image
width={200}
src=""
/>
</Variant>
</Component>
<Component name="Avatar">
<Variant>
<Avatar icon={<UserOutlined />} />
</Variant>
<Variant name="Image">
<Avatar src="https://joeschmoe.io/api/v1/random" />
</Variant>
</Component>
<Component name="Badge">
<Variant>
<Badge count={1}>
</Badge>
</Variant>
</Component>
<Component name="Statistic">
<Variant>
<Statistic title="Title" value={112893} />
</Variant>
</Component>
<Component name="Alert">
<Variant name="Success">
<Alert message="Text" type="success" />
</Variant>
<Variant name="Info">
<Alert message="Text" type="info" />
</Variant>
<Variant name="Warning">
<Alert message="Text" type="warning" />
</Variant>
<Variant name="Error">
<Alert message="Text" type="error" />
</Variant>
</Component>
<Component name='List'>
<Variant>
<List
bordered
dataSource={[]}
renderItem={item => (
<List.Item>
</List.Item>
)}
/>
</Variant>
</Component>
</Category>
<Category name="Icons">
<Component name="Arrow">
<Variant name = 'Up'>
<ArrowUpOutlined />
</Variant>
<Variant name = 'Down'>
<ArrowDownOutlined />
</Variant>
<Variant name = 'Left'>
<ArrowLeftOutlined />
</Variant>
<Variant name = 'Right'>
<ArrowRightOutlined />
</Variant>
</Component>
<Component name = 'Question'>
<Variant>
<QuestionOutlined />
</Variant>
<Variant name = 'Circle'>
<QuestionCircleOutlined />
</Variant>
</Component>
<Component name = 'Plus'>
<Variant>
<PlusOutlined />
</Variant>
<Variant name = 'Circle'>
<PlusCircleOutlined />
</Variant>
</Component>
<Component name = 'Info'>
<Variant>
<InfoOutlined />
</Variant>
<Variant name = 'Circle'>
<InfoCircleOutlined />
</Variant>
</Component>
<Component name = 'Exclamation'>
<Variant>
<ExclamationOutlined />
</Variant>
<Variant name = 'Circle'>
<ExclamationCircleOutlined />
</Variant>
</Component>
<Component name = 'Close'>
<Variant>
<CloseOutlined />
</Variant>
<Variant name = 'Circle'>
<CloseCircleOutlined />
</Variant>
</Component>
<Component name = 'Check'>
<Variant>
<CheckOutlined />
</Variant>
<Variant name = 'Circle'>
<CheckCircleOutlined />
</Variant>
</Component>
<Component name = 'Edit'>
<Variant>
<EditOutlined />
</Variant>
</Component>
<Component name = 'Copy'>
<Variant>
<CopyOutlined />
</Variant>
</Component>
<Component name = 'Delete'>
<Variant>
<DeleteOutlined />
</Variant>
</Component>
<Component name = 'Bars'>
<Variant>
<BarsOutlined />
</Variant>
</Component>
<Component name = 'Bell'>
<Variant>
<BellOutlined />
</Variant>
</Component>
<Component name = 'Clear'>
<Variant>
<ClearOutlined />
</Variant>
</Component>
<Component name = 'Download'>
<Variant>
<DownloadOutlined />
</Variant>
</Component>
<Component name = 'Upload'>
<Variant>
<UploadOutlined />
</Variant>
</Component>
<Component name = 'Sync'>
<Variant>
<SyncOutlined />
</Variant>
</Component>
<Component name = 'Save'>
<Variant>
<SaveOutlined />
</Variant>
</Component>
<Component name = 'Search'>
<Variant>
<SearchOutlined />
</Variant>
</Component>
<Component name = 'Settings'>
<Variant>
<SettingOutlined />
</Variant>
</Component>
<Component name = 'Paperclip'>
<Variant>
<PaperClipOutlined />
</Variant>
</Component>
<Component name = 'Phone'>
<Variant>
<PhoneOutlined />
</Variant>
</Component>
<Component name = 'Mail'>
<Variant>
<MailOutlined />
</Variant>
</Component>
<Component name = 'Home'>
<Variant>
<HomeOutlined />
</Variant>
</Component>
<Component name = 'Contacts'>
<Variant>
<ContactsOutlined />
</Variant>
</Component>
<Component name = 'User'>
<Variant>
<UserOutlined />
</Variant>
<Variant name = 'Add'>
<UserAddOutlined />
</Variant>
<Variant name = 'Remove'>
<UserDeleteOutlined />
</Variant>
</Component>
<Component name = 'Team'>
<Variant>
<TeamOutlined />
</Variant>
</Component>
</Category>
</Palette>
Example #9
Source File: index.tsx From antdp with MIT License | 4 votes |
QuickForm: QuickFormComponent = (props, ref) => {
const {
collapseAttributes,
panelAttributes,
visible = false,
type = 'cardform',
extra,
formDatas,
colspan = 3,
header,
defaultFormLayout = 'vertical',
defaultFormItemLayout = formDefaultFormItemLayout,
size = 'default',
formHide,
initialHide,
...otherProps
} = props;
const [hide] = useFormItemHide(formHide)
hide.setInitialValues(initialHide || {}, true)
const HideFormItemDoM = []; // 隐藏的表单
const FormItemDoM = [];
let rowcolspan: string | any; // col 里的布局
let formitemlayout: string | any; // formitem 的布局
for (var i = 0; i < formDatas.length; i++) {
if (formDatas[i].hideInForm) {
HideFormItemDoM.push(formDatas[i]);
} else {
FormItemDoM.push(formDatas[i]);
}
}
// 计算一个row里排几个表单;
const result = [];
for (let i = 0, j = FormItemDoM.length; i < j; i++) {
if (FormItemDoM[i].full) {
result.push(FormItemDoM.slice(i, i + 1));
} else {
if (FormItemDoM[i + 1] && FormItemDoM[i + 1].full) {
result.push(FormItemDoM.slice(i, i + 1));
} else if (FormItemDoM[i].defaultcolspan) {
result.push(FormItemDoM.slice(i, i + FormItemDoM[i].defaultcolspan));
i = i + FormItemDoM[i].defaultcolspan - 1;
} else {
result.push(FormItemDoM.slice(i, i + colspan));
i = i + colspan - 1;
}
}
}
// 渲染成表单;
const CollapseFormDoM = (item: any, idx: React.Key | null | undefined) => {
const {
label,
name,
attributes,
type,
options,
onlyimg,
defaultFormItemLayout,
full,
defaultRowColspan,
hideInForm,
descItem,
render,
// 用于判断是否需要进行隐藏显示 (在组件外层包裹一层组件用于控制item显示和隐藏)
isHide,
...otherts
} = item;
const dataList = options || [];
const optionDatas =
dataList &&
dataList.length > 0 &&
dataList.map(
(
{ value, label, ...others }: any,
_idx: React.Key | null | undefined,
) => {
if (type === 'select' || type === 'Select') {
return (
<Option value={value} key={_idx} {...others}>
{label}
</Option>
);
} else if (type === 'radio' || type === 'Radio') {
return (
<Radio.Button value={value} key={_idx} {...others}>
{label}
</Radio.Button>
);
}
},
);
const selectOption = optionDatas ? optionDatas : [];
const rowcolspan_num = [
colLayout_one,
colLayout_two,
colLayout_third,
colLayout_fourth,
];
const formitemlayout_num = [
fromItemLayout_conspan_one,
fromItemLayout_conspan_two,
fromItemLayout_conspan_third,
fromItemLayout_conspan_fourth,
];
if (colspan && full) {
rowcolspan = colLayout_one;
if (colspan === 3 || colspan === 4) {
if (props.defaultFormItemLayout) {
// 如果FormCollapse组件上带有defaulFormItemLayout参数
formitemlayout = props.defaultFormItemLayout;
// eslint-disable-next-line max-depth
if (item.defaultFormItemLayout || item.defaultRowColspan) {
// 如果FormCollapse组件内部的某个小组件带有defaulFormItemLayout参数
formitemlayout = item.defaultFormItemLayout;
rowcolspan = item.defaultRowColspan; // 单独的表单col 布局
}
} else if (item.defaultFormItemLayout || item.defaultRowColspan) {
//FormCollapse组件内部只有某个小组件带了defaulFormItemLayout参数
formitemlayout = item.defaultFormItemLayout;
rowcolspan = item.defaultRowColspan; // 单独的表单col 布局
} else {
formitemlayout = fromItemLayout_third_row;
}
} else {
formitemlayout = fromItemLayout_two_row;
}
} else {
rowcolspan = rowcolspan_num[colspan - 1];
if (props.defaultFormItemLayout) {
formitemlayout = props.defaultFormItemLayout;
if (item.defaultFormItemLayout || item.defaultRowColspan) {
// 如果FormCollapse组件内部的某个小组件带有defaultFormItemLayout参数
formitemlayout = item.defaultFormItemLayout;
rowcolspan = item.defaultRowColspan; // 单独的表单col 布局
}
} else if (item.defaultFormItemLayout || item.defaultRowColspan) {
formitemlayout =
item.defaultFormItemLayout || formitemlayout_num[colspan - 1];
rowcolspan = item.defaultRowColspan; // 单独的表单col 布局
} else {
formitemlayout = formitemlayout_num[colspan - 1];
}
}
// 上传图片的按钮展示
const uploadButtonDom = () => {
if (item.attributes.listType === 'picture-card') {
if (item.attributes.imageUrl && item.attributes.imageUrl !== '') {
return (
<img
src={item.attributes.imageUrl}
alt="avatar"
style={{ width: '100%' }}
/>
);
} else if (item.attributes.fileList) {
// 上传的图片大于或等于8张时 并且 没有onlyimg参数,显示icon上传按钮
if (item.attributes.fileList.length >= 8 && !onlyimg) {
return (
<div>
{item.attributes.loading === 'loading' ? (
<LoadingOutlined />
) : (
<PlusOutlined />
)}
<div className="ant-upload-text">上传</div>
</div>
);
// 上传的图片大于或等于maxCount张时 并且 有onlyimg参数,不显示上传按钮
} else if (item.attributes.maxCount && item.attributes.fileList.length >= item.attributes.maxCount && onlyimg) {
return null;
}
return (
<div>
{item.attributes.loading === 'loading' ? (
<LoadingOutlined />
) : (
<PlusOutlined />
)}
<div className="ant-upload-text">上传</div>
</div>
);
}
} else {
return (
<div>
<Button>
<UploadOutlined />
上传
</Button>
</div>
);
}
};
let renderItem = (
<Col
key={idx}
style={{
display: item.hideInForm ? 'none' : 'block',
padding:
defaultFormLayout && defaultFormLayout === 'vertical'
? '0px 12px 8px 12px'
: '0',
}}
className={
defaultFormLayout && defaultFormLayout === 'vertical'
? 'antdp-FormCol'
: ''
}
{...rowcolspan}
>
<FormItem
className="antdp-FormItem"
colon={false}
label={label}
name={name}
{...(defaultFormLayout && defaultFormLayout === 'vertical'
? null
: formitemlayout)}
{...otherts}
>
{name ? (
(() => {
// 组件基础参数
const componentprams = {
size: size ? size : 'small',
...attributes,
};
if (type === 'select' || type === 'Select') {
return (
<Select
dropdownMatchSelectWidth={false}
allowClear
placeholder={
attributes && attributes.disabled ? '' : `请选择${label} `
}
{...componentprams}
>
{selectOption}
</Select>
);
} else if (type === 'radio' || type === 'Radio') {
return (
<Radio.Group size={size ? size : 'small'} {...attributes}>
{selectOption}
</Radio.Group>
);
} else if (type === 'datePicker' || type === 'DatePicker') {
return (
<DatePicker
locale={locale}
style={{ width: '100%' }}
placeholder={
attributes && attributes.disabled ? '' : `请选择${label} `
}
{...componentprams}
/>
);
} else if (type === 'monthPicker' || type === 'MonthPicker') {
return (
<MonthPicker
locale={locale}
style={{ width: '100%' }}
placeholder={
attributes && attributes.disabled ? '' : `请选择${label} `
}
{...componentprams}
/>
);
} else if (type === 'rangePicker' || type === 'RangePicker') {
return (
<RangePicker
locale={locale}
style={{ width: '100%' }}
{...componentprams}
/>
);
} else if (
type === 'timepicker' ||
type === 'timePicker' ||
type === 'TimePicker'
) {
return (
<TimePicker
locale={locale}
style={{ width: '100%' }}
placeholder={
attributes && attributes.disabled ? '' : `请选择${label} `
}
{...componentprams}
/>
);
} else if (type === 'cascader' || type === 'Cascader') {
return (
<Cascader
placeholder={
attributes && attributes.disabled ? '' : `请选择${label} `
}
{...componentprams}
/>
);
} else if (type === 'textarea' || type === 'TextArea') {
return (
<Input.TextArea
placeholder={
attributes && attributes.disabled ? '' : `请输入${label} `
}
{...componentprams}
/>
);
} else if (type === 'inputNumber' || type === 'InputNumber') {
return (
<InputNumber
placeholder={
attributes && attributes.disabled ? '' : `请输入${label} `
}
style={{ width: '100%' }}
{...componentprams}
/>
);
} else if (type === 'treeSelect' || type === 'TreeSelect') {
return (
<TreeSelect
placeholder={
attributes && attributes.disabled ? '' : `请选择${label} `
}
{...componentprams}
/>
);
} else if (type === 'checkbox' || type === 'Checkbox') {
if (
(item.options && item.options.length > 0) ||
(item.option && item.option.length > 0)
) {
return (
<Checkbox.Group
options={item.options || item.option}
{...attributes}
/>
);
}
return (
<Checkbox {...attributes}>
{label || item.checkboxLable}
</Checkbox>
);
} else if (type === 'UploadGrid' || type === 'uploadGrid') {
return (
<UploadGrid {...attributes}>{uploadButtonDom()}</UploadGrid>
);
} else if (type === 'autoComplete' || type === 'AutoComplete') {
return (
<AutoComplete
placeholder={
attributes && attributes.disabled ? '' : `请输入${label} `
}
{...componentprams}
/>
);
} else if (type === 'Password') {
return (
<Input.Password
placeholder={
attributes && attributes.disabled ? '' : `请输入${label} `
}
{...componentprams}
/>
);
} else if (type === 'inputCount' || type === 'InputCount') {
return (
<InputCount
placeholder={
attributes && attributes.disabled ? '' : `请输入${label} `
}
{...attributes}
/>
);
} else if (type === 'render') {
return render && render
} else {
if (
(attributes && attributes.type === 'Search') ||
type === 'InputSearch'
) {
const suffix = (
<AudioOutlined
style={{
fontSize: 16,
color: '#fff',
}}
/>
);
return (
<Search
suffix={suffix}
placeholder={
attributes && attributes.disabled
? ''
: `请输入${label} `
}
{...componentprams}
/>
);
}
return (
<Input
placeholder={
attributes && attributes.disabled ? '' : `请输入${label} `
}
{...componentprams}
/>
);
}
})()
) : (
<Input
placeholder={
attributes && attributes.disabled ? '' : `请输入${label} `
}
size={size}
{...attributes}
/>
)}
</FormItem>
</Col>
)
if (isHide && name) {
return (
<Hide key={idx} name={name}>
{renderItem}
</Hide>
);
}
return renderItem;
};
// 隐藏的表单集合
const hideCollapseForm = HideFormItemDoM.map((item, idx) =>
CollapseFormDoM(item, idx),
);
// 表单集合
const CollapseForm = result.map((it, indix) => {
return (
<Row key={indix}>
{it.map((item, idx) => {
return CollapseFormDoM(item, idx);
})}
</Row>
);
});
// Form+表单集合
const FormDom = (
<HideContext.Provider value={hide} >
<ConfigProvider locale={zhCN}>
<Form
layout={defaultFormLayout ? defaultFormLayout : 'horizontal'}
ref={ref}
{...(defaultFormLayout && defaultFormLayout === 'vertical'
? null
: formitemlayout)}
{...otherProps}
>
<Row>{hideCollapseForm}</Row>
<div>{CollapseForm}</div>
</Form>
</ConfigProvider>
</HideContext.Provider>
);
// type 为 modal时没有折叠,没有标题,直接显示form表单内容
if (type === 'modal') {
return <div style={{ margin: -10 }}>{FormDom}</div>
}
// type 为CardPro 带标题
if (type === 'CardPro') {
return (
<CardPro title={header}>
<div className="antdp-FormBox">{FormDom}</div>
</CardPro>
);
}
// type 为cardform 时 显示表单,分割线 分离每个表单
if (type === 'cardform') {
return (
<div>
<h3 className="antdp-FormTitle">{header}</h3>
{FormDom}
<Divider type="horizontal" className="antdp-FormDivider" />
</div>
);
}
return (
<Collapse
defaultActiveKey={!visible ? ['1'] : ''}
{...collapseAttributes}
className="antdp-mb10"
>
<Panel header={header} key="1" {...panelAttributes} extra={extra}>
{FormDom}
</Panel>
</Collapse>
);
}
Example #10
Source File: TimeRangePicker.spec.tsx From next-basics with GNU General Public License v3.0 | 4 votes |
describe("TimeRangePicker", () => {
it("should work", () => {
const wrapper = mount(<TimeRangePicker format="HH:mm:ss" />);
expect(wrapper.find(TimePicker)).toHaveLength(2);
});
it("should work with init value", () => {
const wrapper = mount(
<TimeRangePicker
format="HH:mm:ss"
value={{ startTime: "01:23:45", endTime: "12:34:56" }}
/>
);
expect(wrapper.find(TimePicker)).toHaveLength(2);
let startTimePicker = wrapper.find(TimePicker).first();
startTimePicker.invoke("onChange")(
moment("00:24:00", "HH:mm:ss"),
// eslint-disable-next-line
// @ts-ignore
"00:24:00"
);
startTimePicker = wrapper.find(TimePicker).first();
expect(startTimePicker.prop("value").format("HH:mm:ss")).toBe("00:24:00");
let endTimePicker = wrapper.find(TimePicker).last();
endTimePicker.invoke("onChange")(
moment("23:59:00", "HH:mm:ss"),
// eslint-disable-next-line
// @ts-ignore
"23:59:00"
);
endTimePicker = wrapper.find(TimePicker).last();
expect(endTimePicker.prop("value").format("HH:mm:ss")).toBe("23:59:00");
});
it("should update value", () => {
const wrapper = mount(<TimeRangePicker format="HH:mm:ss" />);
expect(wrapper.find(TimePicker).first().prop("value")).toBeUndefined();
wrapper.setProps({
value: { startTime: "01:23:45", endTime: "12:34:56" },
});
wrapper.update();
expect(
wrapper.find(TimePicker).first().prop("value").format("HH:mm:ss")
).toBe("01:23:45");
});
it("date range should work", () => {
const fn = jest.fn();
const wrapper = mount(
<TimeRangePicker
format="YYYY-MM-DD HH:mm:ss"
rangeType="dateTime"
onChange={fn}
value={{
startTime: "2020-03-16 01:23:45",
endTime: "2020-03-16 12:34:56",
}}
emitChangeOnInit={true}
/>
);
expect(wrapper.find(DatePicker.RangePicker)).toHaveLength(1);
const rangePicker = wrapper.find(DatePicker.RangePicker).first();
rangePicker.invoke("onChange")([moment(), moment()], ["", ""]);
rangePicker.invoke("onOk")([moment(), moment()]);
rangePicker.invoke("onOpenChange")(true);
rangePicker.invoke("onOpenChange")(false);
expect(fn).toBeCalled();
});
it("date range should work when startTime or endTime undefined", () => {
const fn = jest.fn();
const wrapper = mount(
<TimeRangePicker
format="YYYY-MM-DD HH:mm:ss"
rangeType="dateTime"
onChange={fn}
value={{
startTime: undefined,
endTime: undefined,
}}
/>
);
expect(wrapper.find(DatePicker.RangePicker)).toHaveLength(1);
const rangePicker = wrapper.find(DatePicker.RangePicker).first();
rangePicker.invoke("onChange")([moment(), moment()], ["", ""]);
rangePicker.invoke("onOk")([moment(), moment()]);
rangePicker.invoke("onOpenChange")(true);
rangePicker.invoke("onOpenChange")(false);
expect(fn).toBeCalled();
});
it("date range should work when default value is null", () => {
let mountValue;
const fn = jest.fn((args) => (mountValue = args));
const wrapper = mount(
<TimeRangePicker
rangeType="dateTime"
format="YYYY-MM-DD HH:mm:ss"
onChange={fn}
value={null}
emitChangeOnInit={true}
/>
);
expect(wrapper.find(DatePicker.RangePicker)).toHaveLength(1);
expect(mountValue).toMatchObject({
startTime: moment().format("YYYY-MM-DD ") + "00:00:00",
endTime: moment().format("YYYY-MM-DD ") + "23:59:59",
});
});
});
Example #11
Source File: TimeRangePicker.tsx From next-basics with GNU General Public License v3.0 | 4 votes |
export function RealTimeRangePicker(
props: RealTimeRangePickerProps,
ref: any
): React.ReactElement {
const times = ["time", "hmTime"];
const rangeType = props.rangeType ?? "time";
const today = times.includes(rangeType) ? "" : moment().format("YYYY-MM-DD ");
const initRange = {
startTime: today + INIT_TIME_RANGE.startTime,
endTime: today + INIT_TIME_RANGE.endTime,
};
const { t } = useTranslation(NS_FORMS);
const initValue =
!isEmpty(props.value?.startTime) || !isEmpty(props.value?.endTime)
? props.value
: initRange;
const [startTime, setStartTime] = React.useState(
moment(initValue.startTime, props.format)
);
const [prevStartTime, setPrevStartTime] = React.useState(startTime?.clone());
const [endTime, setEndTime] = React.useState(
moment(initValue.endTime, props.format)
);
const [prevEndTime, setPrevEndTime] = React.useState(endTime?.clone());
const onStartTimeChange = (time: moment.Moment, timeString: string) => {
setStartTime(time);
props.onChange?.({
startTime: timeString,
endTime: endTime?.format(props.format),
});
};
const onEndTimeChange = (time: moment.Moment, timeString: string) => {
setEndTime(time);
props.onChange?.({
endTime: timeString,
startTime: startTime?.format(props.format),
});
};
React.useEffect(() => {
if (props.emitChangeOnInit && !props.value && props.onChange) {
times.includes(rangeType)
? props.onChange(INIT_TIME_RANGE)
: props.onChange(initRange);
}
}, []);
React.useEffect(() => {
if (props.value?.startTime) {
const start = moment(props.value.startTime, props.format);
setStartTime(start);
setPrevStartTime(start);
}
if (props.value?.endTime) {
const end = moment(props.value.endTime, props.format);
setEndTime(end);
setPrevEndTime(end);
}
}, [props.value]);
const timeRange = (
<Input.Group compact className={styles.timeRange}>
<TimePicker
{...{ id: uniqueId("start-time-") }}
onChange={onStartTimeChange}
value={!isEmpty(props.value?.startTime) ? startTime : undefined}
format={props.format}
/>
<Input disabled className={styles.timeRangeSplit} value="~" />
<TimePicker
{...{ id: uniqueId("end-time-") }}
onChange={onEndTimeChange}
value={!isEmpty(props.value?.endTime) ? endTime : undefined}
format={props.format}
/>
</Input.Group>
);
const rangeChange = (
dates: RangeValue<moment.Moment>,
dateStrings: [string, string]
) => {
setStartTime(dates?.[0]);
setEndTime(dates?.[1]);
props.onChange?.({
startTime: dates?.[0].format(props.format),
endTime: dates?.[1].format(props.format),
});
};
const needConfirm = React.useRef(false);
const onOpenChange = (open: boolean) => {
if (!open && needConfirm.current) {
setStartTime(prevStartTime);
setEndTime(prevEndTime);
} else {
needConfirm.current = true;
}
};
const rangeOk = (selectedTime: RangeValue<moment.Moment>) => {
needConfirm.current = false;
const dates = selectedTime;
setPrevStartTime(dates?.[0].clone());
setPrevEndTime(dates?.[1].clone());
props.onChange?.({
startTime: dates?.[0].format(props.format),
endTime: dates?.[1].format(props.format),
});
};
const dateRange = (
<DatePicker.RangePicker
style={{ width: 400 }}
showTime={rangeType === "dateTime"}
value={
!isEmpty(props.value?.startTime) || !isEmpty(props.value?.endTime)
? [startTime, endTime]
: []
}
format={props.format}
onChange={rangeChange}
onOpenChange={onOpenChange}
onOk={rangeOk}
separator={"~"}
suffixIcon={<Icon component={() => <BrickIcon icon="calendar" />} />}
/>
);
const range = times.includes(rangeType) ? timeRange : dateRange;
return <div ref={ref}>{range}</div>;
}
Example #12
Source File: index.tsx From imove with MIT License | 4 votes |
SchemaForm: React.FC<IProps> = (props) => {
const { schema, configData, changeConfigData } = props;
const [form] = Form.useForm();
const onClickSave = (): void => {
changeConfigData(form.getFieldsValue());
message.success('保存成功!');
};
return schema && Object.keys(schema).length > 0 ? (
<Form form={form} initialValues={configData} {...formLayout}>
{schema?.properties &&
Object.keys(schema.properties).map((key: string) => {
const obj = schema.properties[key];
const options: INum[] = obj.enum
? obj.enum.map((item: string, idx: number) => {
return { label: item, value: obj.enumNames[idx] };
})
: [];
let ele = null;
// 输入框
if (
obj.type === 'string' &&
!obj.hasOwnProperty('format') &&
!obj.hasOwnProperty('enum')
) {
ele = <Input />;
}
// 编辑框
if (obj.type === 'string' && obj.format === 'textarea') {
ele = <Input.TextArea />;
}
// switch
if (obj.type === 'boolean' && obj['ui:widget'] === 'switch') {
ele = <Switch />;
}
// 下拉单选
if (
obj.type === 'string' &&
obj.hasOwnProperty('enum') &&
!obj.hasOwnProperty('ui:widget')
) {
ele = (
<Select allowClear>
{options.map((item) => (
<Option key={item.value} value={item.value}>
{item.label}
</Option>
))}
</Select>
);
}
// 下拉多选
if (
obj.type === 'array' &&
obj.hasOwnProperty('enum') &&
obj['ui:widget'] === 'multiSelect'
) {
ele = (
<Select allowClear mode="multiple">
{options.map((item) => (
<Option key={item.value} value={item.value}>
{item.label}
</Option>
))}
</Select>
);
}
// 点击多选
if (
obj.type === 'array' &&
obj.hasOwnProperty('enum') &&
!obj.hasOwnProperty('ui:widget')
) {
ele = <Checkbox.Group options={options} />;
}
// 时间选择
if (obj.type === 'string' && obj.format === 'time') {
ele = <TimePicker defaultValue={moment('00:00:00', 'HH:mm:ss')} />;
}
// 日期范围
if (obj.type === 'range' && obj.format === 'date') {
ele = <DatePicker />;
}
// 日期选择
if (obj.type === 'string' && obj.format === 'date') {
ele = <RangePicker />;
}
return (
<FormItem label={obj.title} name={key} key={key}>
{ele}
</FormItem>
);
})}
<div className={styles.btnWrap}>
<Button
size="small"
className={styles.btn}
type={'primary'}
onClick={onClickSave}
>
保存
</Button>
</div>
</Form>
) : (
<Empty
description={'请编辑投放配置schema'}
image={Empty.PRESENTED_IMAGE_SIMPLE}
/>
);
}
Example #13
Source File: editModal.tsx From fe-v5 with Apache License 2.0 | 4 votes |
editModal: React.FC<Props> = ({ isModalVisible, editModalFinish }) => {
const { t, i18n } = useTranslation();
const [form] = Form.useForm();
const { clusters: clusterList } = useSelector<RootState, CommonStoreState>((state) => state.common);
const [contactList, setInitContactList] = useState([]);
const [notifyGroups, setNotifyGroups] = useState([]);
const [field, setField] = useState<string>('cluster');
const [refresh, setRefresh] = useState(true);
useEffect(() => {
getNotifyChannel();
getGroups('');
return () => {};
}, []);
const enableDaysOfWeekOptions = [t('周日'), t('周一'), t('周二'), t('周三'), t('周四'), t('周五'), t('周六')].map((v, i) => {
return <Option value={String(i)} key={i}>{`${v}`}</Option>;
});
const contactListCheckboxes = contactList.map((c: { key: string; label: string }) => (
<Checkbox value={c.key} key={c.label}>
{c.label}
</Checkbox>
));
const notifyGroupsOptions = notifyGroups.map((ng: any) => (
<Option value={ng.id} key={ng.id}>
{ng.name}
</Option>
));
const getNotifyChannel = async () => {
const res = await getNotifiesList();
let contactList = res || [];
setInitContactList(contactList);
};
const getGroups = async (str) => {
const res = await getTeamInfoList({ query: str });
const data = res.dat || res;
setNotifyGroups(data || []);
};
const debounceFetcher = useCallback(debounce(getGroups, 800), []);
const modelOk = () => {
form.validateFields().then(async (values) => {
const data = { ...values };
switch (values.field) {
case 'enable_time':
data.enable_stime = values.enable_time[0].format('HH:mm');
data.enable_etime = values.enable_time[1].format('HH:mm');
delete data.enable_time;
break;
case 'disabled':
data.disabled = !values.enable_status ? 1 : 0;
delete data.enable_status;
break;
case 'enable_in_bg':
data.enable_in_bg = values.enable_in_bg ? 1 : 0;
break;
case 'callbacks':
data.callbacks = values.callbacks.map((item) => item.url);
break;
case 'notify_recovered':
data.notify_recovered = values.notify_recovered ? 1 : 0;
break;
default:
break;
}
delete data.field;
Object.keys(data).forEach((key) => {
// 因为功能上有清除备注的需求,需要支持传空
if (data[key] === undefined) {
data[key] = '';
}
if (Array.isArray(data[key])) {
data[key] = data[key].join(' ');
}
});
editModalFinish(true, data);
});
};
const editModalClose = () => {
editModalFinish(false);
};
const fieldChange = (val) => {
setField(val);
};
return (
<>
<Modal
title={t('批量更新')}
visible={isModalVisible}
onOk={modelOk}
onCancel={() => {
editModalClose();
}}
>
<Form
{...layout}
form={form}
className='strategy-form'
layout={refresh ? 'horizontal' : 'horizontal'}
initialValues={{
prom_eval_interval: 15,
disabled: 0, // 0:立即启用 1:禁用
enable_status: true, // true:立即启用 false:禁用
notify_recovered: 1, // 1:启用
enable_time: [moment('00:00', 'HH:mm'), moment('23:59', 'HH:mm')],
cluster: clusterList[0] || 'Default', // 生效集群
enable_days_of_week: ['1', '2', '3', '4', '5', '6', '0'],
field: 'cluster',
}}
>
<Form.Item
label={t('字段:')}
name='field'
rules={[
{
required: false,
},
]}
>
<Select suffixIcon={<CaretDownOutlined />} style={{ width: '100%' }} onChange={fieldChange}>
{fields.map((item) => (
<Option key={item.id} value={item.field}>
{item.name}
</Option>
))}
</Select>
</Form.Item>
{(() => {
switch (field) {
case 'note':
return (
<>
<Form.Item
label={t('改为:')}
name='note'
rules={[
{
required: false,
},
]}
>
<Input placeholder={t('请输入规则备注')} />
</Form.Item>
</>
);
case 'runbook_url':
return (
<>
<Form.Item label={t('改为:')} name='runbook_url'>
<Input />
</Form.Item>
</>
);
case 'cluster':
return (
<>
<Form.Item
label={t('改为:')}
name='cluster'
rules={[
{
required: false,
message: t('生效集群不能为空'),
},
]}
>
<Select suffixIcon={<CaretDownOutlined />}>
{clusterList?.map((item) => (
<Option value={item} key={item}>
{item}
</Option>
))}
</Select>
</Form.Item>
</>
);
case 'severity':
return (
<>
<Form.Item
label={t('改为:')}
name='severity'
initialValue={2}
rules={[
{
required: false,
message: t('告警级别不能为空'),
},
]}
>
<Radio.Group>
<Radio value={1}>{t('一级报警')}</Radio>
<Radio value={2}>{t('二级报警')}</Radio>
<Radio value={3}>{t('三级报警')}</Radio>
</Radio.Group>
</Form.Item>
</>
);
case 'disabled':
return (
<>
<Form.Item
label={t('改为:')}
name='enable_status'
rules={[
{
required: false,
message: t('立即启用不能为空'),
},
]}
valuePropName='checked'
>
<Switch />
</Form.Item>
</>
);
case 'enable_in_bg':
return (
<>
<Form.Item label={t('改为:')} name='enable_in_bg' valuePropName='checked'>
<SwitchWithLabel label='根据告警事件中的ident归属关系判断' />
</Form.Item>
</>
);
case 'prom_eval_interval':
return (
<>
<Form.Item
label={t('改为:')}
rules={[
{
required: false,
message: t('执行频率不能为空'),
},
]}
>
<Space>
<Form.Item style={{ marginBottom: 0 }} name='prom_eval_interval' initialValue={15} wrapperCol={{ span: 10 }}>
<InputNumber
min={1}
onChange={(val) => {
setRefresh(!refresh);
}}
/>
</Form.Item>
秒
<Tooltip title={t(`每隔${form.getFieldValue('prom_eval_interval')}秒,把PromQL作为查询条件,去查询后端存储,如果查到了数据就表示当次有监控数据触发了规则`)}>
<QuestionCircleFilled />
</Tooltip>
</Space>
</Form.Item>
</>
);
case 'prom_for_duration':
return (
<>
<Form.Item
label={t('改为:')}
rules={[
{
required: false,
message: t('持续时长不能为空'),
},
]}
>
<Space>
<Form.Item style={{ marginBottom: 0 }} name='prom_for_duration' initialValue={60} wrapperCol={{ span: 10 }}>
<InputNumber min={0} />
</Form.Item>
秒
<Tooltip
title={t(
`通常持续时长大于执行频率,在持续时长内按照执行频率多次执行PromQL查询,每次都触发才生成告警;如果持续时长置为0,表示只要有一次PromQL查询触发阈值,就生成告警`,
)}
>
<QuestionCircleFilled />
</Tooltip>
</Space>
</Form.Item>
</>
);
case 'notify_channels':
return (
<>
<Form.Item label={t('改为:')} name='notify_channels'>
<Checkbox.Group>{contactListCheckboxes}</Checkbox.Group>
</Form.Item>
</>
);
case 'notify_groups':
return (
<>
<Form.Item label={t('改为:')} name='notify_groups'>
<Select mode='multiple' showSearch optionFilterProp='children' filterOption={false} onSearch={(e) => debounceFetcher(e)} onBlur={() => getGroups('')}>
{notifyGroupsOptions}
</Select>
</Form.Item>
</>
);
case 'notify_recovered':
return (
<>
<Form.Item label={t('改为:')} name='notify_recovered' valuePropName='checked'>
<Switch />
</Form.Item>
</>
);
case 'recover_duration':
return (
<>
<Form.Item label={t('改为:')}>
<Space>
<Form.Item
style={{ marginBottom: 0 }}
name='recover_duration'
initialValue={0}
wrapperCol={{ span: 10 }}
rules={[
{
required: false,
message: t('留观时长不能为空'),
},
]}
>
<InputNumber
min={0}
onChange={(val) => {
setRefresh(!refresh);
}}
/>
</Form.Item>
秒
<Tooltip title={t(`持续${form.getFieldValue('recover_duration') || 0}秒没有再次触发阈值才发送恢复通知`)}>
<QuestionCircleFilled />
</Tooltip>
</Space>
</Form.Item>
</>
);
case 'notify_repeat_step':
return (
<>
<Form.Item label={t('改为:')}>
<Space>
<Form.Item
style={{ marginBottom: 0 }}
name='notify_repeat_step'
initialValue={60}
wrapperCol={{ span: 10 }}
rules={[
{
required: false,
message: t('重复发送频率不能为空'),
},
]}
>
<InputNumber
min={0}
onChange={(val) => {
setRefresh(!refresh);
}}
/>
</Form.Item>
分钟
<Tooltip title={t(`如果告警持续未恢复,间隔${form.getFieldValue('notify_repeat_step')}分钟之后重复提醒告警接收组的成员`)}>
<QuestionCircleFilled />
</Tooltip>
</Space>
</Form.Item>
</>
);
case 'callbacks':
return (
<>
<Form.Item label={t('改为:')}>
<Form.List name='callbacks' initialValue={[{}]}>
{(fields, { add, remove }, { errors }) => (
<>
{fields.map((field, index) => (
<Row gutter={[10, 0]} key={field.key}>
<Col span={22}>
<Form.Item name={[field.name, 'url']}>
<Input />
</Form.Item>
</Col>
<Col span={1}>
<MinusCircleOutlined className='control-icon-normal' onClick={() => remove(field.name)} />
</Col>
</Row>
))}
<PlusCircleOutlined className='control-icon-normal' onClick={() => add()} />
</>
)}
</Form.List>
</Form.Item>
</>
);
case 'append_tags':
return (
<>
<Form.Item label='附加标签' name='append_tags' rules={[{ required: false, message: '请填写至少一项标签!' }, isValidFormat]}>
<Select mode='tags' tokenSeparators={[' ']} open={false} placeholder={'标签格式为 key=value ,使用回车或空格分隔'} tagRender={tagRender} />
</Form.Item>
</>
);
case 'enable_time':
return (
<>
<Form.Item
label={t('改为:')}
name='enable_days_of_week'
rules={[
{
required: false,
message: t('生效时间不能为空'),
},
]}
>
<Select mode='tags'>{enableDaysOfWeekOptions}</Select>
</Form.Item>
<Form.Item
name='enable_time'
{...tailLayout}
rules={[
{
required: false,
message: t('生效时间不能为空'),
},
]}
>
<TimePicker.RangePicker
format='HH:mm'
onChange={(val, val2) => {
form.setFieldsValue({
enable_stime: val2[0],
enable_etime: val2[1],
});
}}
/>
</Form.Item>
</>
);
default:
return null;
}
})()}
</Form>
</Modal>
</>
);
}
Example #14
Source File: operateForm.tsx From fe-v5 with Apache License 2.0 | 4 votes |
operateForm: React.FC<Props> = ({ type, detail = {} }) => {
const { t, i18n } = useTranslation();
const history = useHistory(); // 创建的时候默认选中的值
const [form] = Form.useForm();
const { clusters: clusterList } = useSelector<RootState, CommonStoreState>((state) => state.common);
const { curBusiItem } = useSelector<RootState, CommonStoreState>((state) => state.common);
const [contactList, setInitContactList] = useState([]);
const [notifyGroups, setNotifyGroups] = useState<any[]>([]);
const [initVal, setInitVal] = useState<any>({});
const [refresh, setRefresh] = useState(true);
useEffect(() => {
getNotifyChannel();
getGroups('');
return () => {};
}, []);
useEffect(() => {
const data = {
...detail,
enable_time: detail?.enable_stime ? [detail.enable_stime, detail.enable_etime] : [],
enable_status: detail?.disabled === undefined ? true : !detail?.disabled,
};
setInitVal(data);
if (type == 1) {
const groups = (detail.notify_groups_obj ? detail.notify_groups_obj.filter((item) => !notifyGroups.find((i) => item.id === i.id)) : []).concat(notifyGroups);
setNotifyGroups(groups);
}
}, [JSON.stringify(detail)]);
const enableDaysOfWeekOptions = [t('周日'), t('周一'), t('周二'), t('周三'), t('周四'), t('周五'), t('周六')].map((v, i) => {
return <Option value={String(i)} key={i}>{`${v}`}</Option>;
});
const contactListCheckboxes = contactList.map((c: { key: string; label: string }) => (
<Checkbox value={c.key} key={c.label}>
{c.label}
</Checkbox>
));
const notifyGroupsOptions = notifyGroups.map((ng: any) => (
<Option value={String(ng.id)} key={ng.id}>
{ng.name}
</Option>
));
const getNotifyChannel = async () => {
const res = await getNotifiesList();
let contactList = res || [];
setInitContactList(contactList);
};
const getGroups = async (str) => {
const res = await getTeamInfoList({ query: str });
const data = res.dat || res;
const combineData = (detail.notify_groups_obj ? detail.notify_groups_obj.filter((item) => !data.find((i) => item.id === i.id)) : []).concat(data);
setNotifyGroups(combineData || []);
};
const addSubmit = () => {
form.validateFields().then(async (values) => {
const res = await prometheusQuery({ query: values.prom_ql }, values.cluster);
if (res.error) {
notification.error({
message: res.error,
});
return false;
}
const callbacks = values.callbacks.map((item) => item.url);
const data = {
...values,
enable_stime: values.enable_time[0].format('HH:mm'),
enable_etime: values.enable_time[1].format('HH:mm'),
disabled: !values.enable_status ? 1 : 0,
notify_recovered: values.notify_recovered ? 1 : 0,
enable_in_bg: values.enable_in_bg ? 1 : 0,
callbacks,
};
let reqBody,
method = 'Post';
if (type === 1) {
reqBody = data;
method = 'Put';
const res = await EditStrategy(reqBody, curBusiItem.id, detail.id);
if (res.err) {
message.error(res.error);
} else {
message.success(t('编辑成功!'));
history.push('/alert-rules');
}
} else {
reqBody = [data];
const { dat } = await addOrEditStrategy(reqBody, curBusiItem.id, method);
let errorNum = 0;
const msg = Object.keys(dat).map((key) => {
dat[key] && errorNum++;
return dat[key];
});
if (!errorNum) {
message.success(`${type === 2 ? t('告警规则克隆成功') : t('告警规则创建成功')}`);
history.push('/alert-rules');
} else {
message.error(t(msg));
}
}
});
};
const debounceFetcher = useCallback(debounce(getGroups, 800), []);
return (
<div className='operate_con'>
<Form
{...layout}
form={form}
className='strategy-form'
layout={refresh ? 'horizontal' : 'horizontal'}
initialValues={{
prom_eval_interval: 15,
prom_for_duration: 60,
severity: 2,
disabled: 0, // 0:立即启用 1:禁用 待修改
// notify_recovered: 1, // 1:启用
cluster: clusterList[0] || 'Default', // 生效集群
enable_days_of_week: ['1', '2', '3', '4', '5', '6', '0'],
...detail,
enable_in_bg: detail?.enable_in_bg === 1,
enable_time: detail?.enable_stime ? [moment(detail.enable_stime, 'HH:mm'), moment(detail.enable_etime, 'HH:mm')] : [moment('00:00', 'HH:mm'), moment('23:59', 'HH:mm')],
enable_status: detail?.disabled === undefined ? true : !detail?.disabled,
notify_recovered: detail?.notify_recovered === 1 || detail?.notify_recovered === undefined ? true : false, // 1:启用 0:禁用
recover_duration: detail?.recover_duration || 0,
callbacks: !!detail?.callbacks
? detail.callbacks.map((item) => ({
url: item,
}))
: [{}],
}}
>
<Space direction='vertical' style={{ width: '100%' }}>
<Card title={t('基本配置')}>
<Form.Item
label={t('规则标题:')}
name='name'
rules={[
{
required: true,
message: t('规则标题不能为空'),
},
]}
>
<Input placeholder={t('请输入规则标题')} />
</Form.Item>
<Form.Item
label={t('规则备注:')}
name='note'
rules={[
{
required: false,
},
]}
>
<Input placeholder={t('请输入规则备注')} />
</Form.Item>
<Form.Item
label={t('告警级别')}
name='severity'
rules={[
{
required: true,
message: t('告警级别不能为空'),
},
]}
>
<Radio.Group>
<Radio value={1}>{t('一级报警')}</Radio>
<Radio value={2}>{t('二级报警')}</Radio>
<Radio value={3}>{t('三级报警')}</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label={t('生效集群')}
name='cluster'
rules={[
{
required: true,
message: t('生效集群不能为空'),
},
]}
>
<Select suffixIcon={<CaretDownOutlined />}>
{clusterList?.map((item) => (
<Option value={item} key={item}>
{item}
</Option>
))}
</Select>
</Form.Item>
<AdvancedWrap>
<AbnormalDetection form={form} />
</AdvancedWrap>
<Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.cluster !== curValues.cluster}>
{() => {
return (
<Form.Item label='PromQL' className={'Promeql-content'} required>
<Form.Item name='prom_ql' validateTrigger={['onBlur']} trigger='onChange' rules={[{ required: true, message: t('请输入PromQL') }]}>
<PromQLInput
url='/api/n9e/prometheus'
headers={{
'X-Cluster': form.getFieldValue('cluster'),
Authorization: `Bearer ${localStorage.getItem('access_token') || ''}`,
}}
/>
</Form.Item>
</Form.Item>
);
}}
</Form.Item>
<Form.Item required label={t('执行频率')}>
<Space>
<Form.Item
style={{ marginBottom: 0 }}
name='prom_eval_interval'
initialValue={15}
wrapperCol={{ span: 10 }}
rules={[
{
required: true,
message: t('执行频率不能为空'),
},
]}
>
<InputNumber
min={1}
onChange={(val) => {
setRefresh(!refresh);
}}
/>
</Form.Item>
秒
<Tooltip title={t(`每隔${form.getFieldValue('prom_eval_interval')}秒,把PromQL作为查询条件,去查询后端存储,如果查到了数据就表示当次有监控数据触发了规则`)}>
<QuestionCircleFilled />
</Tooltip>
</Space>
</Form.Item>
<Form.Item
required
label={t('持续时长')}
rules={[
{
required: true,
message: t('持续时长不能为空'),
},
]}
>
<Space>
<Form.Item style={{ marginBottom: 0 }} name='prom_for_duration' wrapperCol={{ span: 10 }}>
<InputNumber min={0} />
</Form.Item>
秒
<Tooltip
title={t(
`通常持续时长大于执行频率,在持续时长内按照执行频率多次执行PromQL查询,每次都触发才生成告警;如果持续时长置为0,表示只要有一次PromQL查询触发阈值,就生成告警`,
)}
>
<QuestionCircleFilled />
</Tooltip>
</Space>
</Form.Item>
<Form.Item label='附加标签' name='append_tags' rules={[{ required: false, message: '请填写至少一项标签!' }, isValidFormat]}>
<Select mode='tags' tokenSeparators={[' ']} open={false} placeholder={'标签格式为 key=value ,使用回车或空格分隔'} tagRender={tagRender} />
</Form.Item>
<Form.Item label={t('预案链接')} name='runbook_url'>
<Input />
</Form.Item>
</Card>
<Card title={t('生效配置')}>
<Form.Item
label={t('立即启用')}
name='enable_status'
rules={[
{
required: true,
message: t('立即启用不能为空'),
},
]}
valuePropName='checked'
>
<Switch />
</Form.Item>
<Form.Item
label={t('生效时间')}
name='enable_days_of_week'
rules={[
{
required: true,
message: t('生效时间不能为空'),
},
]}
>
<Select mode='tags'>{enableDaysOfWeekOptions}</Select>
</Form.Item>
<Form.Item
name='enable_time'
{...tailLayout}
rules={[
{
required: true,
message: t('生效时间不能为空'),
},
]}
>
<TimePicker.RangePicker
format='HH:mm'
onChange={(val, val2) => {
form.setFieldsValue({
enable_stime: val2[0],
enable_etime: val2[1],
});
}}
/>
</Form.Item>
<Form.Item label={t('仅在本业务组生效')} name='enable_in_bg' valuePropName='checked'>
<SwitchWithLabel label='根据告警事件中的ident归属关系判断' />
</Form.Item>
</Card>
<Card title={t('通知配置')}>
<Form.Item label={t('通知媒介')} name='notify_channels'>
<Checkbox.Group>{contactListCheckboxes}</Checkbox.Group>
</Form.Item>
<Form.Item label={t('告警接收组')} name='notify_groups'>
<Select mode='multiple' showSearch optionFilterProp='children' filterOption={false} onSearch={(e) => debounceFetcher(e)} onBlur={() => getGroups('')}>
{notifyGroupsOptions}
</Select>
</Form.Item>
<Form.Item label={t('启用恢复通知')}>
<Space>
<Form.Item name='notify_recovered' valuePropName='checked' style={{ marginBottom: 0 }}>
<Switch />
</Form.Item>
<Tooltip title={t(`告警恢复时也发送通知`)}>
<QuestionCircleFilled />
</Tooltip>
</Space>
</Form.Item>
<Form.Item label={t('留观时长')} required>
<Space>
<Form.Item style={{ marginBottom: 0 }} name='recover_duration' initialValue={0} wrapperCol={{ span: 10 }}>
<InputNumber
min={0}
onChange={(val) => {
setRefresh(!refresh);
}}
/>
</Form.Item>
秒
<Tooltip title={t(`持续${form.getFieldValue('recover_duration')}秒没有再次触发阈值才发送恢复通知`)}>
<QuestionCircleFilled />
</Tooltip>
</Space>
</Form.Item>
<Form.Item label={t('重复发送频率')} required>
<Space>
<Form.Item
style={{ marginBottom: 0 }}
name='notify_repeat_step'
initialValue={60}
wrapperCol={{ span: 10 }}
rules={[
{
required: true,
message: t('重复发送频率不能为空'),
},
]}
>
<InputNumber
min={0}
onChange={(val) => {
setRefresh(!refresh);
}}
/>
</Form.Item>
分钟
<Tooltip title={t(`如果告警持续未恢复,间隔${form.getFieldValue('notify_repeat_step')}分钟之后重复提醒告警接收组的成员`)}>
<QuestionCircleFilled />
</Tooltip>
</Space>
</Form.Item>
<Form.Item label={t('回调地址')}>
<Form.List name='callbacks' initialValue={[{}]}>
{(fields, { add, remove }) => (
<>
{fields.map((field) => (
<Row gutter={[10, 0]} key={field.key}>
<Col span={22}>
<Form.Item name={[field.name, 'url']} fieldKey={[field.fieldKey, 'url']}>
<Input />
</Form.Item>
</Col>
<Col span={1}>
<MinusCircleOutlined className='control-icon-normal' onClick={() => remove(field.name)} />
</Col>
</Row>
))}
<PlusCircleOutlined className='control-icon-normal' onClick={() => add()} />
</>
)}
</Form.List>
</Form.Item>
</Card>
<Form.Item
// {...tailLayout}
style={{
marginTop: 20,
}}
>
<Button type='primary' onClick={addSubmit} style={{ marginRight: '8px' }}>
{type === 1 ? t('编辑') : type === 2 ? t('克隆') : t('创建')}
</Button>
{type === 1 && (
<Button
danger
style={{ marginRight: '8px' }}
onClick={() => {
Modal.confirm({
title: t('是否删除该告警规则?'),
onOk: () => {
deleteStrategy([detail.id], curBusiItem.id).then(() => {
message.success(t('删除成功'));
history.push('/alert-rules');
});
},
onCancel() {},
});
}}
>
{t('删除')}
</Button>
)}
<Button
onClick={() => {
history.push('/alert-rules');
}}
>
{t('取消')}
</Button>
</Form.Item>
</Space>
</Form>
</div>
);
}