forked from datahub-project/datahub
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ingestion) Build out UI form for Snowflake Managed Ingestion (da…
- Loading branch information
1 parent
d134ee8
commit fe17418
Showing
8 changed files
with
841 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
datahub-web-react/src/app/ingest/source/builder/RecipeBuilder.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { Button, message } from 'antd'; | ||
import React, { useState } from 'react'; | ||
import YAML from 'yamljs'; | ||
import { CodeOutlined, FormOutlined } from '@ant-design/icons'; | ||
import styled from 'styled-components/macro'; | ||
import { ANTD_GRAY } from '../../../entity/shared/constants'; | ||
import { YamlEditor } from './YamlEditor'; | ||
import RecipeForm from './RecipeForm/RecipeForm'; | ||
|
||
export const ControlsContainer = styled.div` | ||
display: flex; | ||
justify-content: space-between; | ||
margin-top: 8px; | ||
`; | ||
|
||
const BorderedSection = styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
padding-bottom: 16px; | ||
border: solid ${ANTD_GRAY[4]} 0.5px; | ||
`; | ||
|
||
const StyledButton = styled(Button)<{ isSelected: boolean }>` | ||
${(props) => | ||
props.isSelected && | ||
` | ||
color: #1890ff; | ||
&:focus { | ||
color: #1890ff; | ||
} | ||
`} | ||
`; | ||
|
||
const ButtonsWrapper = styled.div` | ||
display: flex; | ||
justify-content: flex-end; | ||
margin-bottom: 10px; | ||
`; | ||
|
||
interface Props { | ||
type: string; | ||
isEditing: boolean; | ||
displayRecipe: string; | ||
setStagedRecipe: (recipe: string) => void; | ||
onClickNext: () => void; | ||
goToPrevious?: () => void; | ||
} | ||
|
||
function RecipeBuilder(props: Props) { | ||
const { type, isEditing, displayRecipe, setStagedRecipe, onClickNext, goToPrevious } = props; | ||
|
||
const [isViewingForm, setIsViewingForm] = useState(true); | ||
|
||
function switchViews(isFormView: boolean) { | ||
try { | ||
YAML.parse(displayRecipe); | ||
setIsViewingForm(isFormView); | ||
} catch (e) { | ||
const messageText = (e as any).parsedLine | ||
? `Fix line ${(e as any).parsedLine} in your recipe` | ||
: 'Please fix your recipe'; | ||
message.warn(`Found invalid YAML. ${messageText} in order to switch views.`); | ||
} | ||
} | ||
|
||
return ( | ||
<div> | ||
<ButtonsWrapper> | ||
<StyledButton type="text" isSelected={isViewingForm} onClick={() => switchViews(true)}> | ||
<FormOutlined /> Form | ||
</StyledButton> | ||
<StyledButton type="text" isSelected={!isViewingForm} onClick={() => switchViews(false)}> | ||
<CodeOutlined /> YAML | ||
</StyledButton> | ||
</ButtonsWrapper> | ||
{isViewingForm && ( | ||
<RecipeForm | ||
type={type} | ||
isEditing={isEditing} | ||
displayRecipe={displayRecipe} | ||
setStagedRecipe={setStagedRecipe} | ||
onClickNext={onClickNext} | ||
goToPrevious={goToPrevious} | ||
/> | ||
)} | ||
{!isViewingForm && ( | ||
<> | ||
<BorderedSection> | ||
<YamlEditor initialText={displayRecipe} onChange={setStagedRecipe} /> | ||
</BorderedSection> | ||
<ControlsContainer> | ||
<Button disabled={isEditing} onClick={goToPrevious}> | ||
Previous | ||
</Button> | ||
<Button onClick={onClickNext}>Next</Button> | ||
</ControlsContainer> | ||
</> | ||
)} | ||
</div> | ||
); | ||
} | ||
|
||
export default RecipeBuilder; |
115 changes: 115 additions & 0 deletions
115
datahub-web-react/src/app/ingest/source/builder/RecipeForm/FormField.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { Button, Checkbox, Form, Input, Tooltip } from 'antd'; | ||
import React from 'react'; | ||
import styled from 'styled-components/macro'; | ||
import { MinusCircleOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons'; | ||
import { FieldType, RecipeField } from './utils'; | ||
import { ANTD_GRAY } from '../../../../entity/shared/constants'; | ||
|
||
const Label = styled.div` | ||
font-weight: bold; | ||
padding-bottom: 8px; | ||
`; | ||
|
||
const StyledButton = styled(Button)` | ||
color: ${ANTD_GRAY[7]}; | ||
width: 80%; | ||
`; | ||
|
||
const StyledQuestion = styled(QuestionCircleOutlined)` | ||
color: rgba(0, 0, 0, 0.45); | ||
margin-left: 4px; | ||
`; | ||
|
||
const StyledRemoveIcon = styled(MinusCircleOutlined)` | ||
font-size: 14px; | ||
margin-left: 10px; | ||
`; | ||
|
||
const StyledFormItem = styled(Form.Item)<{ alignLeft: boolean; removeMargin: boolean }>` | ||
margin-bottom: ${(props) => (props.removeMargin ? '0' : '16px')}; | ||
${(props) => | ||
props.alignLeft && | ||
` | ||
.ant-form-item { | ||
flex-direction: row; | ||
} | ||
.ant-form-item-label { | ||
padding: 0; | ||
margin-right: 10px; | ||
} | ||
`} | ||
`; | ||
|
||
const ListWrapper = styled.div<{ removeMargin: boolean }>` | ||
margin-bottom: ${(props) => (props.removeMargin ? '0' : '16px')}; | ||
`; | ||
|
||
interface ListFieldProps { | ||
field: RecipeField; | ||
removeMargin?: boolean; | ||
} | ||
|
||
function ListField({ field, removeMargin }: ListFieldProps) { | ||
return ( | ||
<Form.List name={field.name}> | ||
{(fields, { add, remove }) => ( | ||
<ListWrapper removeMargin={!!removeMargin}> | ||
<Label> | ||
{field.label} | ||
<Tooltip overlay={field.tooltip}> | ||
<StyledQuestion /> | ||
</Tooltip> | ||
</Label> | ||
{fields.map((item) => ( | ||
<Form.Item key={item.fieldKey} style={{ marginBottom: '10px' }}> | ||
<Form.Item {...item} noStyle> | ||
<Input style={{ width: '80%' }} /> | ||
</Form.Item> | ||
<StyledRemoveIcon onClick={() => remove(item.name)} /> | ||
</Form.Item> | ||
))} | ||
<StyledButton type="dashed" onClick={() => add()} style={{ width: '80%' }} icon={<PlusOutlined />}> | ||
Add pattern | ||
</StyledButton> | ||
</ListWrapper> | ||
)} | ||
</Form.List> | ||
); | ||
} | ||
|
||
interface Props { | ||
field: RecipeField; | ||
removeMargin?: boolean; | ||
} | ||
|
||
function FormField(props: Props) { | ||
const { field, removeMargin } = props; | ||
|
||
if (field.type === FieldType.LIST) return <ListField field={field} removeMargin={removeMargin} />; | ||
|
||
const isBoolean = field.type === FieldType.BOOLEAN; | ||
const input = isBoolean ? <Checkbox /> : <Input />; | ||
const valuePropName = isBoolean ? 'checked' : 'value'; | ||
const getValueFromEvent = isBoolean ? undefined : (e) => (e.target.value === '' ? null : e.target.value); | ||
|
||
return ( | ||
<StyledFormItem | ||
style={isBoolean ? { flexDirection: 'row', alignItems: 'center' } : {}} | ||
label={field.label} | ||
name={field.name} | ||
tooltip={field.tooltip} | ||
rules={field.rules || undefined} | ||
valuePropName={valuePropName} | ||
getValueFromEvent={getValueFromEvent} | ||
alignLeft={isBoolean} | ||
removeMargin={!!removeMargin} | ||
> | ||
{input} | ||
</StyledFormItem> | ||
); | ||
} | ||
|
||
export default FormField; |
Oops, something went wrong.