Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Select form field #8815

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open

Add Select form field #8815

wants to merge 14 commits into from

Conversation

Devessier
Copy link
Contributor

@Devessier Devessier commented Nov 29, 2024

Closes #8815

I took inspiration from existing parts of the codebase. Please, see the comments I left below.

Remaining questions:

  • I'm not sure about the best way to handle hotkey scopes in the components easily
CleanShot.2024-12-02.at.17.35.16.mp4

@Devessier Devessier self-assigned this Nov 29, 2024

{draftValue.editingMode === 'edit' ? (
<SelectableList
selectableListId={SINGLE_RECORD_SELECT_BASE_LIST}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about the id to use here.

}: FormSelectFieldInputProps) => {
const inputId = useId();

const hotkeyScope = InlineCellHotkeyScope.InlineCell;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to create another scope

@Devessier Devessier marked this pull request as ready for review December 2, 2024 16:38
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR introduces a new select form field component and integrates it into the existing form system, with support for variable picking and keyboard navigation.

  • Added new FormSelectFieldInput.tsx component with hotkey scope management and variable support
  • Modified FormFieldInput.tsx to handle select fields using isFieldSelect guard
  • Updated WorkflowEditActionFormRecordCreate.tsx to support metadata-driven select fields
  • Added type safety improvements by replacing FieldMetadataItem with FieldDefinition<FieldMetadata>
  • Implemented keyboard navigation and filtering in select field component following existing patterns

3 file(s) reviewed, 6 comment(s)
Edit PR Review Bot Settings | Greptile

@@ -23,7 +26,6 @@ export const FormFieldInput = ({
}: FormFieldInputProps) => {
return isFieldNumber(field) ? (
<FormNumberFieldInput
key={field.id}
label={field.label}
defaultValue={defaultValue as string | number | undefined}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: type assertion could be unsafe here - consider adding runtime type checking before the cast

onPersist={onPersist}
field={field}
VariablePicker={VariablePicker}
/>
) : null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: consider throwing an error for unhandled field types instead of silently returning null

Comment on lines +98 to +101
const onCancel = () => {
if (draftValue.type !== 'static') {
throw new Error('Can only be called when editing a static value');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider handling this error case more gracefully instead of throwing - could lead to runtime crashes if state gets corrupted

Comment on lines +195 to +198
const optionIds = [
`No ${field.label}`,
...filteredOptions.map((option) => option.value),
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: optionIds includes 'No {field.label}' but this ID isn't handled in handleSelectEnter - could cause undefined behavior

Copy link
Contributor

@martmull martmull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, some comments

Comment on lines 218 to 222
<Tag
preventShrink
color={selectedOption.color}
text={selectedOption.label}
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use <SelectFieldDisplay component here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've considered using the existing FieldInput components; however, they rely on the FieldContext. See the useSelectField and useSelectFieldDisplay hooks. We don't currently provide this context. If we do, we will couple the FormFieldInputs with the usual FieldInputs. This might be considered, but it is an opinionated choice.

I duplicated most of SelectFieldInput's code. We might reconsider this and find a way to reduce the code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might create a new components to decouple the component design definition from the fieldContext. Thus we will be able to factorize

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a component to unify what we display for the display mode of a select input.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, is it possible to do the same with the input mode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you like to extract this part?

<SelectableList
  selectableListId={SINGLE_RECORD_SELECT_BASE_LIST}
  selectableItemIdArray={optionIds}
  hotkeyScope={hotkeyScope}
  onEnter={(itemId) => {
    const option = filteredOptions.find(
      (option) => option.value === itemId,
    );
    if (isDefined(option)) {
      onSubmit?.(() => persistField(option.value));
      resetSelectedItem();
    }
  }}
>
  <SelectInput
    parentRef={selectWrapperRef}
    onOptionSelected={handleSubmit}
    options={fieldDefinition.metadata.options}
    onCancel={onCancel}
    defaultOption={selectedOption}
    onFilterChange={setFilteredOptions}
    onClear={
      fieldDefinition.metadata.isNullable ? handleClearField : undefined
    }
    clearLabel={fieldDefinition.label}
    hotkeyScope={hotkeyScope}
  />
</SelectableList>

I'm not sure it would be great, as I would end up with a component taking +10 unrelated props.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, the SelectableList and SelectInput components are well enough abstracted. They own the design decisions. These two components are meta components with business logic. I don't think we need to go deeper into the refactoring.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think yes you should extract this part into packages/twenty-front/src/modules/ui/field/input/components in a SelectInput component and use that in FormSelectFieldInput and in SelectFieldInput

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like this, the way to display or modify a SELECT value is uniquely defined throughout the application

</StyledDisplayModeContainer>

{draftValue.editingMode === 'edit' ? (
<SelectableList
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, can you use <SelectFieldInput component?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same reasoning applies here: #8815 (comment)

Comment on lines 66 to 73
const availableFieldMetadataItems = objectMetadataItem.fields
.filter(
(fieldMetadataItem) =>
fieldMetadataItem.type !== FieldMetadataType.Relation &&
!fieldMetadataItem.isSystem &&
fieldMetadataItem.isActive,
)
.sort((fieldMetadataItemA, fieldMetadataItemB) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thomtrp, we should not forget to do that also for Update Action when developed

Comment on lines 1 to 13
import { Tag, ThemeColor } from 'twenty-ui';

type SelectFieldDisplayContentProps = {
color: ThemeColor;
label: string;
};

export const SelectFieldDisplayContent = ({
color,
label,
}: SelectFieldDisplayContentProps) => {
return <Tag preventShrink color={color} text={label} />;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think you should move that to packages/twenty-front/src/modules/ui/field/display/components and call it SelectDisplay

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants