Skip to content

Commit

Permalink
Migrate trigger form params to new UI (apache#45270)
Browse files Browse the repository at this point in the history
* Add flexible form fields to trigger form WIP

* Add flexible form fields to trigger form WIP

* Fix dropdown issue

* Add support for arrays of strings

* Add support for advanced arrays

* Remove placeholder for string

* Add support for proposals in string fields

* Add support for multi-select dropdowns

* Add support for object fields

* Add support for number fields

* Add support for multiline text fields

* Use other component for multi-select which displays the values

* Implement support for form sections

* Implement support for form sections

* Add an alert that form ist not fully implemented

* Review feedback - safe string handling

* Review feedback, remove reloading warnings, move selector functions to separate TSX file

* Review feedback - rename components for shorter name

* Review feedback - adjust hard-coded color for boder in CodeMirror

* Rework default field values

* Move form elements into a common accorion

* Review feedback

* Review feedback, next round

* Remove un-needed placeholders in date-time picker
  • Loading branch information
jscheffl authored and dauinh committed Jan 23, 2025
1 parent b011514 commit 312148b
Show file tree
Hide file tree
Showing 20 changed files with 843 additions and 12 deletions.
5 changes: 3 additions & 2 deletions airflow/example_dags/example_params_ui_tutorial.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
# If you want to have a list box with proposals but not enforcing a fixed list
# then you can use the examples feature of JSON schema
"proposals": Param(
"some value",
"Alpha",
type="string",
title="Field with proposals",
description="You can use JSON schema examples's to generate drop down selection boxes "
Expand Down Expand Up @@ -169,6 +169,7 @@
"multiline_text": Param(
"A multiline text Param\nthat will keep the newline\ncharacters in its value.",
description="This field allows for multiline text input. The returned value will be a single with newline (\\n) characters kept intact.",
title="Multiline text",
type=["string", "null"],
format="multiline",
),
Expand Down Expand Up @@ -205,7 +206,7 @@
[schema description (string)](https://json-schema.org/understanding-json-schema/reference/string.html)
for more details""",
minLength=10,
maxLength=20,
maxLength=30,
section="JSON Schema validation options",
),
"checked_number": Param(
Expand Down
52 changes: 52 additions & 0 deletions airflow/ui/src/components/FlexibleForm/FieldAdvancedArray.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { json } from "@codemirror/lang-json";
import { githubLight, githubDark } from "@uiw/codemirror-themes-all";
import CodeMirror from "@uiw/react-codemirror";

import { useColorMode } from "src/context/colorMode";

import type { FlexibleFormElementProps } from ".";

export const FieldAdvancedArray = ({ name, param }: FlexibleFormElementProps) => {
const { colorMode } = useColorMode();

return (
<CodeMirror
basicSetup={{
autocompletion: true,
bracketMatching: true,
foldGutter: true,
lineNumbers: true,
}}
extensions={[json()]}
height="200px"
id={`element_${name}`}
style={{
border: "1px solid var(--chakra-colors-border)",
borderRadius: "8px",
outline: "none",
padding: "2px",
width: "100%",
}}
theme={colorMode === "dark" ? githubDark : githubLight}
value={JSON.stringify(param.value ?? [], undefined, 2)}
/>
);
};
29 changes: 29 additions & 0 deletions airflow/ui/src/components/FlexibleForm/FieldBool.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import type { FlexibleFormElementProps } from ".";
import { Switch } from "../ui";

export const FieldBool = ({ name, param }: FlexibleFormElementProps) => (
<Switch
colorPalette="blue"
defaultChecked={Boolean(param.value)}
id={`element_${name}`}
name={`element_${name}`}
/>
);
31 changes: 31 additions & 0 deletions airflow/ui/src/components/FlexibleForm/FieldDateTime.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Input, type InputProps } from "@chakra-ui/react";

import type { FlexibleFormElementProps } from ".";

export const FieldDateTime = ({ name, param, ...rest }: FlexibleFormElementProps & InputProps) => (
<Input
defaultValue={typeof param.value === "string" ? param.value : undefined}
id={`element_${name}`}
name={`element_${name}`}
size="sm"
type={rest.type}
/>
);
66 changes: 66 additions & 0 deletions airflow/ui/src/components/FlexibleForm/FieldDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { createListCollection } from "@chakra-ui/react/collection";
import { useRef } from "react";

import { Select } from "src/components/ui";

import type { FlexibleFormElementProps } from ".";

const labelLookup = (key: string, valuesDisplay: Record<string, string> | undefined): string => {
if (valuesDisplay && typeof valuesDisplay === "object") {
return valuesDisplay[key] ?? key;
}

return key;
};
const enumTypes = ["string", "number", "integer"];

export const FieldDropdown = ({ name, param }: FlexibleFormElementProps) => {
const selectOptions = createListCollection({
items:
param.schema.enum?.map((value) => ({
label: labelLookup(value, param.schema.values_display),
value,
})) ?? [],
});
const contentRef = useRef<HTMLDivElement>(null);

return (
<Select.Root
collection={selectOptions}
defaultValue={enumTypes.includes(typeof param.value) ? [String(param.value)] : undefined}
id={`element_${name}`}
name={`element_${name}`}
ref={contentRef}
size="sm"
>
<Select.Trigger>
<Select.ValueText placeholder="Select Value" />
</Select.Trigger>
<Select.Content portalRef={contentRef}>
{selectOptions.items.map((option) => (
<Select.Item item={option} key={option.value}>
{option.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
);
};
60 changes: 60 additions & 0 deletions airflow/ui/src/components/FlexibleForm/FieldMultiSelect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Select as ReactSelect } from "chakra-react-select";
import { useState } from "react";

import type { FlexibleFormElementProps } from ".";

const labelLookup = (key: string, valuesDisplay: Record<string, string> | undefined): string => {
if (valuesDisplay && typeof valuesDisplay === "object") {
return valuesDisplay[key] ?? key;
}

return key;
};

export const FieldMultiSelect = ({ name, param }: FlexibleFormElementProps) => {
const [selectedOptions, setSelectedOptions] = useState(
Array.isArray(param.value)
? (param.value as Array<string>).map((value) => ({
label: labelLookup(value, param.schema.values_display),
value,
}))
: [],
);

return (
<ReactSelect
aria-label="Select one or multiple values"
id={`element_${name}`}
isClearable
isMulti
name={`element_${name}`}
onChange={(newValue) => setSelectedOptions([...newValue])}
options={
param.schema.examples?.map((value) => ({
label: labelLookup(value, param.schema.values_display),
value,
})) ?? []
}
size="sm"
value={selectedOptions}
/>
);
};
31 changes: 31 additions & 0 deletions airflow/ui/src/components/FlexibleForm/FieldMultilineText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Textarea } from "@chakra-ui/react";

import type { FlexibleFormElementProps } from ".";

export const FieldMultilineText = ({ name, param }: FlexibleFormElementProps) => (
<Textarea
defaultValue={String(param.value ?? "")}
id={`element_${name}`}
name={`element_${name}`}
rows={6}
size="sm"
/>
);
34 changes: 34 additions & 0 deletions airflow/ui/src/components/FlexibleForm/FieldNumber.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import type { FlexibleFormElementProps } from ".";
import { NumberInputField, NumberInputRoot } from "../ui/NumberInput";

export const FieldNumber = ({ name, param }: FlexibleFormElementProps) => (
<NumberInputRoot
allowMouseWheel
defaultValue={String(param.value ?? "")}
id={`element_${name}`}
max={param.schema.maximum ?? undefined}
min={param.schema.minimum ?? undefined}
name={`element_${name}`}
size="sm"
>
<NumberInputField />
</NumberInputRoot>
);
52 changes: 52 additions & 0 deletions airflow/ui/src/components/FlexibleForm/FieldObject.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { json } from "@codemirror/lang-json";
import { githubLight, githubDark } from "@uiw/codemirror-themes-all";
import CodeMirror from "@uiw/react-codemirror";

import { useColorMode } from "src/context/colorMode";

import type { FlexibleFormElementProps } from ".";

export const FieldObject = ({ name, param }: FlexibleFormElementProps) => {
const { colorMode } = useColorMode();

return (
<CodeMirror
basicSetup={{
autocompletion: true,
bracketMatching: true,
foldGutter: true,
lineNumbers: true,
}}
extensions={[json()]}
height="200px"
id={`element_${name}`}
style={{
border: "1px solid var(--chakra-colors-border)",
borderRadius: "8px",
outline: "none",
padding: "2px",
width: "100%",
}}
theme={colorMode === "dark" ? githubDark : githubLight}
value={JSON.stringify(param.value ?? {}, undefined, 2)}
/>
);
};
Loading

0 comments on commit 312148b

Please sign in to comment.