Add suggested values for variable dropdown (#9437)

<img width="378" alt="Capture d’écran 2025-01-07 à 15 37 20"
src="https://github.com/user-attachments/assets/c15abcac-684a-4c3b-ad12-62cf91afe927"
/>

Here is a first version:
- simple fields have a suggested value
- composite do not, but sub values of composite do
- json, arrays or complex values do not
This commit is contained in:
Thomas Trompette
2025-01-10 16:18:37 +01:00
committed by GitHub
parent 5648c3b31c
commit 92c119ed43
12 changed files with 89 additions and 24 deletions

View File

@ -1,6 +1,6 @@
{
"name": "twenty-e2e-testing",
"version": "0.40.0-canary",
"version": "0.35.4",
"description": "",
"author": "",
"private": true,

View File

@ -1,6 +1,6 @@
{
"name": "twenty-emails",
"version": "0.40.0-canary",
"version": "0.35.4",
"description": "",
"author": "",
"private": true,

View File

@ -1,6 +1,6 @@
{
"name": "twenty-front",
"version": "0.40.0-canary",
"version": "0.35.4",
"private": true,
"type": "module",
"scripts": {

View File

@ -1,5 +1,5 @@
import { ServerlessFunctionExecutionStatus } from '~/generated-metadata/graphql';
import { createFamilyState } from '@/ui/utilities/state/utils/createFamilyState';
import { ServerlessFunctionExecutionStatus } from '~/generated-metadata/graphql';
export type ServerlessFunctionTestData = {
input: { [field: string]: any };
@ -13,8 +13,7 @@ export type ServerlessFunctionTestData = {
height: number;
};
export const DEFAULT_OUTPUT_VALUE =
'Enter an input above then press "run Function"';
export const DEFAULT_OUTPUT_VALUE = 'Enter an input above then press "Test"';
export const serverlessFunctionTestDataFamilyState = createFamilyState<
ServerlessFunctionTestData,

View File

@ -145,15 +145,18 @@ export const WorkflowVariablesDropdownFieldItems = ({
/>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer>
{filteredOptions.map(([key, value]) => (
{filteredOptions.map(([key, subStep]) => (
<MenuItemSelect
key={key}
selected={false}
hovered={false}
onClick={() => handleSelectField(key)}
text={value.label || key}
hasSubMenu={!value.isLeaf}
LeftIcon={value.icon ? getIcon(value.icon) : undefined}
text={subStep.label || key}
hasSubMenu={!subStep.isLeaf}
LeftIcon={subStep.icon ? getIcon(subStep.icon) : undefined}
contextualText={
subStep.isLeaf ? subStep?.value?.toString() : undefined
}
/>
))}
</DropdownMenuItemsContainer>

View File

@ -1,6 +1,6 @@
{
"name": "twenty-server",
"version": "0.40.0-canary",
"version": "0.35.4",
"description": "",
"author": "",
"private": true,

View File

@ -7,13 +7,16 @@ type FakeValueTypes =
| Date
| FakeValueTypes[]
| FieldMetadataType
| { [key: string]: FakeValueTypes };
| { [key: string]: FakeValueTypes }
| null;
export const generateFakeValue = (valueType: string): FakeValueTypes => {
type TypeClassification = 'Primitive' | 'FieldMetadataType';
const generatePrimitiveValue = (valueType: string): FakeValueTypes => {
if (valueType === 'string') {
return 'generated-string-value';
return 'My text';
} else if (valueType === 'number') {
return 1;
return 20;
} else if (valueType === 'boolean') {
return true;
} else if (valueType === 'Date') {
@ -38,9 +41,51 @@ export const generateFakeValue = (valueType: string): FakeValueTypes => {
});
return objData;
} else if (valueType === FieldMetadataType.TEXT) {
return 'My text';
} else {
return 'generated-string-value';
return null;
}
};
const generateFieldMetadataTypeValue = (
valueType: string,
): FakeValueTypes | null => {
// composite types do not need to be generated
switch (valueType) {
case FieldMetadataType.TEXT:
return 'My text';
case FieldMetadataType.NUMBER:
return 20;
case FieldMetadataType.BOOLEAN:
return true;
case FieldMetadataType.DATE:
return '01/23/2025';
case FieldMetadataType.DATE_TIME:
return '01/23/2025 15:16';
case FieldMetadataType.ADDRESS:
return '123 Main St, Anytown, CA 12345';
case FieldMetadataType.FULL_NAME:
return 'Tim Cook';
case FieldMetadataType.RAW_JSON:
return null;
case FieldMetadataType.RICH_TEXT:
return 'My rich text';
case FieldMetadataType.UUID:
return '123e4567-e89b-12d3-a456-426614174000';
default:
return null;
}
};
export const generateFakeValue = (
valueType: string,
classification: TypeClassification = 'Primitive',
): FakeValueTypes => {
switch (classification) {
case 'Primitive':
return generatePrimitiveValue(valueType);
case 'FieldMetadataType':
return generateFieldMetadataTypeValue(valueType);
default:
return null;
}
};

View File

@ -25,7 +25,7 @@ const generateObjectRecordFields = (
type: field.type,
icon: field.icon,
label: field.label,
value: generateFakeValue(field.type),
value: generateFakeValue(field.type, 'FieldMetadataType'),
};
} else {
acc[field.name] = {
@ -37,7 +37,7 @@ const generateObjectRecordFields = (
isLeaf: true,
type: property.type,
label: camelToTitleCase(property.name),
value: generateFakeValue(property.type),
value: generateFakeValue(property.type, 'FieldMetadataType'),
};
return acc;

View File

@ -1,6 +1,6 @@
{
"name": "twenty-ui",
"version": "0.40.0-canary",
"version": "0.35.4",
"type": "module",
"main": "./src/index.ts",
"exports": {

View File

@ -47,6 +47,7 @@ type MenuItemSelectProps = {
disabled?: boolean;
hovered?: boolean;
hasSubMenu?: boolean;
contextualText?: string;
};
export const MenuItemSelect = ({
@ -59,6 +60,7 @@ export const MenuItemSelect = ({
disabled,
hovered,
hasSubMenu = false,
contextualText,
}: MenuItemSelectProps) => {
const theme = useTheme();
@ -73,8 +75,13 @@ export const MenuItemSelect = ({
aria-selected={selected}
aria-disabled={disabled}
>
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
<MenuItemLeftContent
LeftIcon={LeftIcon}
text={text}
contextualText={contextualText}
/>
{selected && needIconCheck && <IconCheck size={theme.icon.size.md} />}
{hasSubMenu && (
<IconChevronRight
size={theme.icon.size.sm}

View File

@ -14,6 +14,10 @@ import {
StyledMenuItemLeftContent,
} from './StyledMenuItemBase';
const StyledMainText = styled.div`
flex-shrink: 0;
`;
const StyledContextualText = styled.div`
color: ${({ theme }) => theme.color.gray35};
font-family: inherit;
@ -29,6 +33,7 @@ const StyledContextualText = styled.div`
white-space: nowrap;
padding-left: ${({ theme }) => theme.spacing(1)};
flex-shrink: 1;
`;
type MenuItemLeftContentProps = {
@ -67,7 +72,13 @@ export const MenuItemLeftContent = ({
<LeftIcon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
)}
<StyledMenuItemLabel hasLeftIcon={!!LeftIcon}>
{isString(text) ? <OverflowingTextWithTooltip text={text} /> : text}
{isString(text) ? (
<StyledMainText>
<OverflowingTextWithTooltip text={text} />
</StyledMainText>
) : (
text
)}
{isString(contextualText) ? (
<StyledContextualText>{`· ${contextualText}`}</StyledContextualText>
) : (

View File

@ -1,6 +1,6 @@
{
"name": "twenty-website",
"version": "0.40.0-canary",
"version": "0.35.4",
"private": true,
"scripts": {
"nx": "NX_DEFAULT_PROJECT=twenty-website node ../../node_modules/nx/bin/nx.js",