There are many fields so I will cut my work in several small PRs. Here, I updated the following fields: - [x] `FormBooleanFieldInput` - [x] `FormCurrencyFieldInput` - [x] `FormNumberFieldInput` - [x] `FormDateFieldInput` - [x] `FormDateTimeFieldInput` - [x] `FormMultiSelectFieldInput` - [x] `FormSelectFieldInput` The updates in the components are relatively small. I wrote Storybook tests, and this is why the PR is quite big. The changes in the components should mostly the same. I added a disabled state to some inputs. I created a specialized `VariableChip` as its styles started diverging from the original `SortOrFilterChip`.
This commit is contained in:
committed by
GitHub
parent
b81879dead
commit
9ebe519e66
@ -1,5 +1,6 @@
|
||||
import { FormAddressFieldInput } from '@/object-record/record-field/form-types/components/FormAddressFieldInput';
|
||||
import { FormBooleanFieldInput } from '@/object-record/record-field/form-types/components/FormBooleanFieldInput';
|
||||
import { FormCurrencyFieldInput } from '@/object-record/record-field/form-types/components/FormCurrencyFieldInput';
|
||||
import { FormDateFieldInput } from '@/object-record/record-field/form-types/components/FormDateFieldInput';
|
||||
import { FormDateTimeFieldInput } from '@/object-record/record-field/form-types/components/FormDateTimeFieldInput';
|
||||
import { FormEmailsFieldInput } from '@/object-record/record-field/form-types/components/FormEmailsFieldInput';
|
||||
@ -12,7 +13,6 @@ import { FormRawJsonFieldInput } from '@/object-record/record-field/form-types/c
|
||||
import { FormSelectFieldInput } from '@/object-record/record-field/form-types/components/FormSelectFieldInput';
|
||||
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
|
||||
import { FormUuidFieldInput } from '@/object-record/record-field/form-types/components/FormUuidFieldInput';
|
||||
import { FormCurrencyFieldInput } from '@/object-record/record-field/form-types/components/FormCurrencyFieldInput';
|
||||
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
|
||||
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
|
||||
import {
|
||||
@ -27,6 +27,7 @@ import {
|
||||
} from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { isFieldAddress } from '@/object-record/record-field/types/guards/isFieldAddress';
|
||||
import { isFieldBoolean } from '@/object-record/record-field/types/guards/isFieldBoolean';
|
||||
import { isFieldCurrency } from '@/object-record/record-field/types/guards/isFieldCurrency';
|
||||
import { isFieldDate } from '@/object-record/record-field/types/guards/isFieldDate';
|
||||
import { isFieldDateTime } from '@/object-record/record-field/types/guards/isFieldDateTime';
|
||||
import { isFieldEmails } from '@/object-record/record-field/types/guards/isFieldEmails';
|
||||
@ -39,7 +40,6 @@ import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFiel
|
||||
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
|
||||
import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText';
|
||||
import { isFieldUuid } from '@/object-record/record-field/types/guards/isFieldUuid';
|
||||
import { isFieldCurrency } from '@/object-record/record-field/types/guards/isFieldCurrency';
|
||||
import { JsonValue } from 'type-fest';
|
||||
|
||||
type FormFieldInputProps = {
|
||||
@ -47,6 +47,7 @@ type FormFieldInputProps = {
|
||||
defaultValue: JsonValue;
|
||||
onPersist: (value: JsonValue) => void;
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
export const FormFieldInput = ({
|
||||
@ -54,6 +55,7 @@ export const FormFieldInput = ({
|
||||
defaultValue,
|
||||
onPersist,
|
||||
VariablePicker,
|
||||
readonly,
|
||||
}: FormFieldInputProps) => {
|
||||
return isFieldNumber(field) ? (
|
||||
<FormNumberFieldInput
|
||||
@ -62,6 +64,7 @@ export const FormFieldInput = ({
|
||||
onPersist={onPersist}
|
||||
placeholder={field.label}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldBoolean(field) ? (
|
||||
<FormBooleanFieldInput
|
||||
@ -69,6 +72,7 @@ export const FormFieldInput = ({
|
||||
defaultValue={defaultValue as string | boolean | undefined}
|
||||
onPersist={onPersist}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldText(field) ? (
|
||||
<FormTextFieldInput
|
||||
@ -77,6 +81,7 @@ export const FormFieldInput = ({
|
||||
onPersist={onPersist}
|
||||
placeholder={field.label}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldSelect(field) ? (
|
||||
<FormSelectFieldInput
|
||||
@ -86,6 +91,7 @@ export const FormFieldInput = ({
|
||||
VariablePicker={VariablePicker}
|
||||
options={field.metadata.options}
|
||||
clearLabel={field.label}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldFullName(field) ? (
|
||||
<FormFullNameFieldInput
|
||||
@ -93,6 +99,7 @@ export const FormFieldInput = ({
|
||||
defaultValue={defaultValue as FieldFullNameValue | undefined}
|
||||
onPersist={onPersist}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldAddress(field) ? (
|
||||
<FormAddressFieldInput
|
||||
@ -100,6 +107,7 @@ export const FormFieldInput = ({
|
||||
defaultValue={defaultValue as FieldAddressValue | undefined}
|
||||
onPersist={onPersist}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldLinks(field) ? (
|
||||
<FormLinksFieldInput
|
||||
@ -107,6 +115,7 @@ export const FormFieldInput = ({
|
||||
defaultValue={defaultValue as FieldLinksValue | undefined}
|
||||
onPersist={onPersist}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldEmails(field) ? (
|
||||
<FormEmailsFieldInput
|
||||
@ -114,6 +123,7 @@ export const FormFieldInput = ({
|
||||
defaultValue={defaultValue as FieldEmailsValue | undefined}
|
||||
onPersist={onPersist}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldPhones(field) ? (
|
||||
<FormPhoneFieldInput
|
||||
@ -121,6 +131,7 @@ export const FormFieldInput = ({
|
||||
defaultValue={defaultValue as FieldPhonesValue | undefined}
|
||||
onPersist={onPersist}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldDate(field) ? (
|
||||
<FormDateFieldInput
|
||||
@ -128,6 +139,7 @@ export const FormFieldInput = ({
|
||||
defaultValue={defaultValue as string | undefined}
|
||||
onPersist={onPersist}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldDateTime(field) ? (
|
||||
<FormDateTimeFieldInput
|
||||
@ -135,6 +147,7 @@ export const FormFieldInput = ({
|
||||
defaultValue={defaultValue as string | undefined}
|
||||
onPersist={onPersist}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldMultiSelect(field) ? (
|
||||
<FormMultiSelectFieldInput
|
||||
@ -143,6 +156,7 @@ export const FormFieldInput = ({
|
||||
onPersist={onPersist}
|
||||
VariablePicker={VariablePicker}
|
||||
options={field.metadata.options}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldRawJson(field) ? (
|
||||
<FormRawJsonFieldInput
|
||||
@ -151,6 +165,7 @@ export const FormFieldInput = ({
|
||||
onPersist={onPersist}
|
||||
placeholder={field.label}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldUuid(field) ? (
|
||||
<FormUuidFieldInput
|
||||
@ -159,6 +174,7 @@ export const FormFieldInput = ({
|
||||
onPersist={onPersist}
|
||||
placeholder={field.label}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : isFieldCurrency(field) ? (
|
||||
<FormCurrencyFieldInput
|
||||
@ -166,6 +182,7 @@ export const FormFieldInput = ({
|
||||
defaultValue={defaultValue as FormFieldCurrencyValue | null}
|
||||
onPersist={onPersist}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
) : null;
|
||||
};
|
||||
|
||||
@ -85,7 +85,7 @@ export const FormBooleanFieldInput = ({
|
||||
|
||||
<FormFieldInputRowContainer>
|
||||
<FormFieldInputInputContainer
|
||||
hasRightElement={isDefined(VariablePicker)}
|
||||
hasRightElement={isDefined(VariablePicker) && !readonly}
|
||||
>
|
||||
{draftValue.type === 'static' ? (
|
||||
<StyledBooleanInputContainer>
|
||||
@ -98,12 +98,12 @@ export const FormBooleanFieldInput = ({
|
||||
) : (
|
||||
<VariableChip
|
||||
rawVariableName={draftValue.value}
|
||||
onRemove={handleUnlinkVariable}
|
||||
onRemove={readonly ? undefined : handleUnlinkVariable}
|
||||
/>
|
||||
)}
|
||||
</FormFieldInputInputContainer>
|
||||
|
||||
{VariablePicker ? (
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
|
||||
@ -1,19 +1,20 @@
|
||||
import { useMemo } from 'react';
|
||||
import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode';
|
||||
import { FormFieldCurrencyValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
|
||||
import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { FormNestedFieldInputContainer } from '@/object-record/record-field/form-types/components/FormNestedFieldInputContainer';
|
||||
import { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput';
|
||||
import { FormSelectFieldInput } from '@/object-record/record-field/form-types/components/FormSelectFieldInput';
|
||||
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
|
||||
import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode';
|
||||
import { FormFieldCurrencyValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { SETTINGS_FIELD_CURRENCY_CODES } from '@/settings/data-model/constants/SettingsFieldCurrencyCodes';
|
||||
import { InputLabel } from '@/ui/input/components/InputLabel';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
type FormCurrencyFieldInputProps = {
|
||||
label?: string;
|
||||
defaultValue?: FormFieldCurrencyValue | null;
|
||||
onPersist: (value: FormFieldCurrencyValue) => void;
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
export const FormCurrencyFieldInput = ({
|
||||
@ -21,6 +22,7 @@ export const FormCurrencyFieldInput = ({
|
||||
defaultValue,
|
||||
onPersist,
|
||||
VariablePicker,
|
||||
readonly,
|
||||
}: FormCurrencyFieldInputProps) => {
|
||||
const currencies = useMemo(() => {
|
||||
return Object.entries(SETTINGS_FIELD_CURRENCY_CODES).map(
|
||||
@ -59,6 +61,7 @@ export const FormCurrencyFieldInput = ({
|
||||
options={currencies}
|
||||
clearLabel={'Currency Code'}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
<FormNumberFieldInput
|
||||
label="Amount Micros"
|
||||
@ -66,6 +69,7 @@ export const FormCurrencyFieldInput = ({
|
||||
onPersist={handleAmountMicrosChange}
|
||||
VariablePicker={VariablePicker}
|
||||
placeholder="Set 3210000 for 3.21$"
|
||||
readonly={readonly}
|
||||
/>
|
||||
</FormNestedFieldInputContainer>
|
||||
</FormFieldInputContainer>
|
||||
|
||||
@ -6,6 +6,7 @@ type FormDateFieldInputProps = {
|
||||
defaultValue: string | undefined;
|
||||
onPersist: (value: string | null) => void;
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
export const FormDateFieldInput = ({
|
||||
@ -13,6 +14,7 @@ export const FormDateFieldInput = ({
|
||||
defaultValue,
|
||||
onPersist,
|
||||
VariablePicker,
|
||||
readonly,
|
||||
}: FormDateFieldInputProps) => {
|
||||
return (
|
||||
<FormDateTimeFieldInput
|
||||
@ -21,6 +23,7 @@ export const FormDateFieldInput = ({
|
||||
defaultValue={defaultValue}
|
||||
onPersist={onPersist}
|
||||
VariablePicker={VariablePicker}
|
||||
readonly={readonly}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -46,6 +46,10 @@ const StyledDateInputAbsoluteContainer = styled.div`
|
||||
const StyledDateInput = styled.input<{ hasError?: boolean }>`
|
||||
${TEXT_INPUT_STYLE}
|
||||
|
||||
&:disabled {
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
}
|
||||
|
||||
${({ hasError, theme }) =>
|
||||
hasError &&
|
||||
css`
|
||||
@ -76,6 +80,7 @@ type FormDateTimeFieldInputProps = {
|
||||
defaultValue: string | undefined;
|
||||
onPersist: (value: string | null) => void;
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
export const FormDateTimeFieldInput = ({
|
||||
@ -84,6 +89,7 @@ export const FormDateTimeFieldInput = ({
|
||||
defaultValue,
|
||||
onPersist,
|
||||
VariablePicker,
|
||||
readonly,
|
||||
}: FormDateTimeFieldInputProps) => {
|
||||
const { timeZone } = useContext(UserContext);
|
||||
|
||||
@ -338,6 +344,7 @@ export const FormDateTimeFieldInput = ({
|
||||
onFocus={handleInputFocus}
|
||||
onChange={handleInputChange}
|
||||
onKeyDown={handleInputKeydown}
|
||||
disabled={readonly}
|
||||
/>
|
||||
|
||||
{draftValue.mode === 'edit' ? (
|
||||
@ -362,12 +369,12 @@ export const FormDateTimeFieldInput = ({
|
||||
) : (
|
||||
<VariableChip
|
||||
rawVariableName={draftValue.value}
|
||||
onRemove={handleUnlinkVariable}
|
||||
onRemove={readonly ? undefined : handleUnlinkVariable}
|
||||
/>
|
||||
)}
|
||||
</StyledInputContainer>
|
||||
|
||||
{VariablePicker ? (
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
|
||||
@ -24,17 +24,21 @@ type FormMultiSelectFieldInputProps = {
|
||||
options: SelectOption[];
|
||||
onPersist: (value: FieldMultiSelectValue | string) => void;
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
const StyledDisplayModeContainer = styled.button`
|
||||
width: 100%;
|
||||
const StyledDisplayModeReadonlyContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: flex;
|
||||
font-family: inherit;
|
||||
padding-inline: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledDisplayModeContainer = styled(StyledDisplayModeReadonlyContainer)`
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&[data-open='true'] {
|
||||
@ -54,6 +58,7 @@ export const FormMultiSelectFieldInput = ({
|
||||
options,
|
||||
onPersist,
|
||||
VariablePicker,
|
||||
readonly,
|
||||
}: FormMultiSelectFieldInputProps) => {
|
||||
const inputId = useId();
|
||||
|
||||
@ -164,26 +169,37 @@ export const FormMultiSelectFieldInput = ({
|
||||
|
||||
<FormFieldInputRowContainer>
|
||||
<FormFieldInputInputContainer
|
||||
hasRightElement={isDefined(VariablePicker)}
|
||||
hasRightElement={isDefined(VariablePicker) && !readonly}
|
||||
>
|
||||
{draftValue.type === 'static' ? (
|
||||
<StyledDisplayModeContainer
|
||||
data-open={draftValue.editingMode === 'edit'}
|
||||
onClick={handleDisplayModeClick}
|
||||
>
|
||||
<VisibilityHidden>Edit</VisibilityHidden>
|
||||
readonly ? (
|
||||
<StyledDisplayModeReadonlyContainer>
|
||||
{isDefined(selectedOptions) && (
|
||||
<MultiSelectDisplay
|
||||
values={selectedNames}
|
||||
options={selectedOptions}
|
||||
/>
|
||||
)}
|
||||
</StyledDisplayModeReadonlyContainer>
|
||||
) : (
|
||||
<StyledDisplayModeContainer
|
||||
data-open={draftValue.editingMode === 'edit'}
|
||||
onClick={handleDisplayModeClick}
|
||||
>
|
||||
<VisibilityHidden>Edit</VisibilityHidden>
|
||||
|
||||
{isDefined(selectedOptions) ? (
|
||||
<MultiSelectDisplay
|
||||
values={selectedNames}
|
||||
options={selectedOptions}
|
||||
/>
|
||||
) : null}
|
||||
</StyledDisplayModeContainer>
|
||||
{isDefined(selectedOptions) && (
|
||||
<MultiSelectDisplay
|
||||
values={selectedNames}
|
||||
options={selectedOptions}
|
||||
/>
|
||||
)}
|
||||
</StyledDisplayModeContainer>
|
||||
)
|
||||
) : (
|
||||
<VariableChip
|
||||
rawVariableName={draftValue.value}
|
||||
onRemove={handleUnlinkVariable}
|
||||
onRemove={readonly ? undefined : handleUnlinkVariable}
|
||||
/>
|
||||
)}
|
||||
</FormFieldInputInputContainer>
|
||||
@ -202,7 +218,7 @@ export const FormMultiSelectFieldInput = ({
|
||||
)}
|
||||
</StyledSelectInputContainer>
|
||||
|
||||
{VariablePicker && (
|
||||
{VariablePicker && !readonly && (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
|
||||
@ -26,6 +26,7 @@ type FormNumberFieldInputProps = {
|
||||
onPersist: (value: number | null | string) => void;
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
hint?: string;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
export const FormNumberFieldInput = ({
|
||||
@ -35,6 +36,7 @@ export const FormNumberFieldInput = ({
|
||||
onPersist,
|
||||
VariablePicker,
|
||||
hint,
|
||||
readonly,
|
||||
}: FormNumberFieldInputProps) => {
|
||||
const inputId = useId();
|
||||
|
||||
@ -102,7 +104,7 @@ export const FormNumberFieldInput = ({
|
||||
|
||||
<FormFieldInputRowContainer>
|
||||
<FormFieldInputInputContainer
|
||||
hasRightElement={isDefined(VariablePicker)}
|
||||
hasRightElement={isDefined(VariablePicker) && !readonly}
|
||||
>
|
||||
{draftValue.type === 'static' ? (
|
||||
<StyledInput
|
||||
@ -112,16 +114,17 @@ export const FormNumberFieldInput = ({
|
||||
copyButton={false}
|
||||
hotkeyScope="record-create"
|
||||
onChange={handleChange}
|
||||
disabled={readonly}
|
||||
/>
|
||||
) : (
|
||||
<VariableChip
|
||||
rawVariableName={draftValue.value}
|
||||
onRemove={handleUnlinkVariable}
|
||||
onRemove={readonly ? undefined : handleUnlinkVariable}
|
||||
/>
|
||||
)}
|
||||
</FormFieldInputInputContainer>
|
||||
|
||||
{VariablePicker ? (
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
|
||||
@ -26,17 +26,21 @@ type FormSelectFieldInputProps = {
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
options: SelectOption[];
|
||||
clearLabel?: string;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
const StyledDisplayModeContainer = styled.button`
|
||||
width: 100%;
|
||||
const StyledDisplayModeReadonlyContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: flex;
|
||||
font-family: inherit;
|
||||
padding-inline: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledDisplayModeContainer = styled(StyledDisplayModeReadonlyContainer)`
|
||||
cursor: pointer;
|
||||
|
||||
&:hover,
|
||||
&[data-open='true'] {
|
||||
@ -57,6 +61,7 @@ export const FormSelectFieldInput = ({
|
||||
VariablePicker,
|
||||
options,
|
||||
clearLabel,
|
||||
readonly,
|
||||
}: FormSelectFieldInputProps) => {
|
||||
const inputId = useId();
|
||||
|
||||
@ -213,32 +218,42 @@ export const FormSelectFieldInput = ({
|
||||
hasRightElement={isDefined(VariablePicker)}
|
||||
>
|
||||
{draftValue.type === 'static' ? (
|
||||
<>
|
||||
readonly ? (
|
||||
<StyledDisplayModeReadonlyContainer>
|
||||
{isDefined(selectedOption) && (
|
||||
<SelectDisplay
|
||||
color={selectedOption.color ?? 'transparent'}
|
||||
label={selectedOption.label}
|
||||
Icon={selectedOption.icon ?? undefined}
|
||||
/>
|
||||
)}
|
||||
</StyledDisplayModeReadonlyContainer>
|
||||
) : (
|
||||
<StyledDisplayModeContainer
|
||||
data-open={draftValue.editingMode === 'edit'}
|
||||
onClick={handleDisplayModeClick}
|
||||
>
|
||||
<VisibilityHidden>Edit</VisibilityHidden>
|
||||
|
||||
{isDefined(selectedOption) ? (
|
||||
{isDefined(selectedOption) && (
|
||||
<SelectDisplay
|
||||
color={selectedOption.color ?? 'transparent'}
|
||||
label={selectedOption.label}
|
||||
Icon={selectedOption.icon ?? undefined}
|
||||
isUsedInForm
|
||||
/>
|
||||
) : null}
|
||||
)}
|
||||
</StyledDisplayModeContainer>
|
||||
</>
|
||||
)
|
||||
) : (
|
||||
<VariableChip
|
||||
rawVariableName={draftValue.value}
|
||||
onRemove={handleUnlinkVariable}
|
||||
onRemove={readonly ? undefined : handleUnlinkVariable}
|
||||
/>
|
||||
)}
|
||||
</FormFieldInputInputContainer>
|
||||
<StyledSelectInputContainer>
|
||||
{draftValue.type === 'static' &&
|
||||
{!readonly &&
|
||||
draftValue.type === 'static' &&
|
||||
draftValue.editingMode === 'edit' && (
|
||||
<OverlayContainer>
|
||||
<SelectInput
|
||||
@ -258,7 +273,7 @@ export const FormSelectFieldInput = ({
|
||||
)}
|
||||
</StyledSelectInputContainer>
|
||||
|
||||
{VariablePicker && (
|
||||
{VariablePicker && !readonly && (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
onVariableSelect={handleVariableTagInsert}
|
||||
|
||||
@ -63,7 +63,7 @@ export const FormTextFieldInput = ({
|
||||
|
||||
<FormFieldInputRowContainer multiline={multiline}>
|
||||
<FormFieldInputInputContainer
|
||||
hasRightElement={isDefined(VariablePicker)}
|
||||
hasRightElement={isDefined(VariablePicker) && !readonly}
|
||||
multiline={multiline}
|
||||
>
|
||||
<TextVariableEditor
|
||||
@ -73,7 +73,7 @@ export const FormTextFieldInput = ({
|
||||
/>
|
||||
</FormFieldInputInputContainer>
|
||||
|
||||
{VariablePicker ? (
|
||||
{VariablePicker && !readonly ? (
|
||||
<VariablePicker
|
||||
inputId={inputId}
|
||||
multiline={multiline}
|
||||
|
||||
@ -1,27 +1,86 @@
|
||||
import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
|
||||
import { extractVariableLabel } from '@/workflow/workflow-variables/utils/extractVariableLabel';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
import { IconX, isDefined } from 'twenty-ui';
|
||||
|
||||
export const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const StyledChip = styled.div<{ deletable: boolean }>`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.accent.quaternary};
|
||||
border: 1px solid ${({ theme }) => theme.accent.tertiary};
|
||||
border-radius: 4px;
|
||||
color: ${({ theme }) => theme.color.blue};
|
||||
height: 26px;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-shrink: 0;
|
||||
column-gap: ${({ theme }) => theme.spacing(1)};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
padding: ${({ theme }) => theme.spacing(0.5)};
|
||||
padding-left: ${({ theme }) => theme.spacing(1)};
|
||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
|
||||
${({ theme, deletable }) =>
|
||||
!deletable &&
|
||||
css`
|
||||
padding-right: ${theme.spacing(1)};
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledDelete = styled.button`
|
||||
box-sizing: border-box;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
user-select: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => theme.accent.secondary};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
}
|
||||
`;
|
||||
|
||||
type VariableChipProps = {
|
||||
rawVariableName: string;
|
||||
onRemove: () => void;
|
||||
onRemove?: () => void;
|
||||
};
|
||||
|
||||
export const VariableChip = ({
|
||||
rawVariableName,
|
||||
onRemove,
|
||||
}: VariableChipProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<SortOrFilterChip
|
||||
labelValue={extractVariableLabel(rawVariableName)}
|
||||
onRemove={onRemove}
|
||||
/>
|
||||
<StyledChip deletable={isDefined(onRemove)}>
|
||||
{extractVariableLabel(rawVariableName)}
|
||||
|
||||
{onRemove ? (
|
||||
<StyledDelete onClick={onRemove}>
|
||||
<IconX size={theme.icon.size.sm} stroke={theme.icon.stroke.sm} />
|
||||
</StyledDelete>
|
||||
) : null}
|
||||
</StyledChip>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { within } from '@storybook/test';
|
||||
import { expect, userEvent, within } from '@storybook/test';
|
||||
import { FormBooleanFieldInput } from '../FormBooleanFieldInput';
|
||||
|
||||
const meta: Meta<typeof FormBooleanFieldInput> = {
|
||||
@ -54,3 +54,37 @@ export const FalseByDefault: Story = {
|
||||
await canvas.findByText('False');
|
||||
},
|
||||
};
|
||||
|
||||
export const WithVariablePicker: Story = {
|
||||
args: {
|
||||
VariablePicker: () => <div>VariablePicker</div>,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const variablePicker = await canvas.findByText('VariablePicker');
|
||||
|
||||
expect(variablePicker).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
readonly: true,
|
||||
defaultValue: false,
|
||||
VariablePicker: () => <div>VariablePicker</div>,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const toggle = await canvas.findByText('False');
|
||||
expect(toggle).toBeVisible();
|
||||
|
||||
await userEvent.click(toggle);
|
||||
|
||||
expect(toggle).toHaveTextContent('False');
|
||||
|
||||
const variablePicker = canvas.queryByText('VariablePicker');
|
||||
expect(variablePicker).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { FormCurrencyFieldInput } from '../FormCurrencyFieldInput';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { FieldCurrencyValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { CurrencyCode } from '@/object-record/record-field/types/CurrencyCode';
|
||||
import { within } from '@storybook/test';
|
||||
import { FieldCurrencyValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, within } from '@storybook/test';
|
||||
import { FormCurrencyFieldInput } from '../FormCurrencyFieldInput';
|
||||
|
||||
const meta: Meta<typeof FormCurrencyFieldInput> = {
|
||||
title: 'UI/Data/Field/Form/Input/FormCurrencyFieldInput',
|
||||
@ -31,3 +31,57 @@ export const Default: Story = {
|
||||
await canvas.findByText('Amount Micros');
|
||||
},
|
||||
};
|
||||
|
||||
export const WithVariable: Story = {
|
||||
args: {
|
||||
label: 'Salary',
|
||||
defaultValue: {
|
||||
currencyCode: CurrencyCode.USD,
|
||||
amountMicros: '{{a.b.c}}',
|
||||
},
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const currency = await canvas.findByText(/USD/);
|
||||
expect(currency).toBeVisible();
|
||||
|
||||
const amountVariable = await canvas.findByText('c');
|
||||
expect(amountVariable).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
export const WithVariablePicker: Story = {
|
||||
args: {
|
||||
VariablePicker: () => <div>VariablePicker</div>,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const variablePickers = await canvas.findAllByText('VariablePicker');
|
||||
|
||||
expect(variablePickers).toHaveLength(2);
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
label: 'Salary',
|
||||
defaultValue: defaultSalaryValue,
|
||||
VariablePicker: () => <div>VariablePicker</div>,
|
||||
readonly: true,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const currency = await canvas.findByText(/USD/);
|
||||
expect(currency).toBeVisible();
|
||||
|
||||
const amountInput = await canvas.findByDisplayValue('44000000');
|
||||
expect(amountInput).toBeVisible();
|
||||
expect(amountInput).toBeDisabled();
|
||||
|
||||
const variablePickers = canvas.queryAllByText('VariablePicker');
|
||||
expect(variablePickers).toHaveLength(0);
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { MAX_DATE } from '@/ui/input/components/internal/date/constants/MaxDate';
|
||||
import { MIN_DATE } from '@/ui/input/components/internal/date/constants/MinDate';
|
||||
import { parseDateToString } from '@/ui/input/components/internal/date/utils/parseDateToString';
|
||||
import { expect } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import {
|
||||
expect,
|
||||
fn,
|
||||
userEvent,
|
||||
waitFor,
|
||||
@ -323,7 +323,9 @@ export const SwitchesToStandaloneVariable: Story = {
|
||||
const variableTag = await canvas.findByText('test');
|
||||
expect(variableTag).toBeVisible();
|
||||
|
||||
const removeVariableButton = canvas.getByTestId(/^remove-icon/);
|
||||
const removeVariableButton = canvasElement.querySelector(
|
||||
'button .tabler-icon-x',
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
userEvent.click(removeVariableButton),
|
||||
@ -372,3 +374,33 @@ export const ClickingOutsideDoesNotResetInputState: Story = {
|
||||
expect(input).toHaveDisplayValue(defaultValueAsDisplayString.slice(0, -2));
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
label: 'Created At',
|
||||
defaultValue: `${currentYear}-12-09T13:20:19.631Z`,
|
||||
onPersist: fn(),
|
||||
readonly: true,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const input = await canvas.findByDisplayValue('12/09/' + currentYear);
|
||||
expect(input).toBeDisabled();
|
||||
},
|
||||
};
|
||||
|
||||
export const DisabledWithVariable: Story = {
|
||||
args: {
|
||||
label: 'Created At',
|
||||
defaultValue: `{{a.b.c}}`,
|
||||
onPersist: fn(),
|
||||
readonly: true,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const variableChip = await canvas.findByText('c');
|
||||
expect(variableChip).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
@ -352,7 +352,9 @@ export const SwitchesToStandaloneVariable: Story = {
|
||||
const variableTag = await canvas.findByText('test');
|
||||
expect(variableTag).toBeVisible();
|
||||
|
||||
const removeVariableButton = canvas.getByTestId(/^remove-icon/);
|
||||
const removeVariableButton = canvasElement.querySelector(
|
||||
'button .tabler-icon-x',
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
userEvent.click(removeVariableButton),
|
||||
@ -401,3 +403,35 @@ export const ClickingOutsideDoesNotResetInputState: Story = {
|
||||
expect(input).toHaveDisplayValue(defaultValueAsDisplayString.slice(0, -2));
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
label: 'Created At',
|
||||
defaultValue: `${currentYear}-12-09T13:20:19.631Z`,
|
||||
onPersist: fn(),
|
||||
readonly: true,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const input = await canvas.findByDisplayValue(
|
||||
new RegExp(`12/09/${currentYear} \\d{2}:20`),
|
||||
);
|
||||
expect(input).toBeDisabled();
|
||||
},
|
||||
};
|
||||
|
||||
export const DisabledWithVariable: Story = {
|
||||
args: {
|
||||
label: 'Created At',
|
||||
defaultValue: `{{a.b.c}}`,
|
||||
onPersist: fn(),
|
||||
readonly: true,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const variableChip = await canvas.findByText('c');
|
||||
expect(variableChip).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { expect } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { within } from '@storybook/test';
|
||||
import { fn, userEvent, within } from '@storybook/test';
|
||||
import { FormMultiSelectFieldInput } from '../FormMultiSelectFieldInput';
|
||||
|
||||
const meta: Meta<typeof FormMultiSelectFieldInput> = {
|
||||
@ -48,3 +49,87 @@ export const Default: Story = {
|
||||
await canvas.findByText('Work Policy 2');
|
||||
},
|
||||
};
|
||||
|
||||
export const WithVariablePicker: Story = {
|
||||
args: {
|
||||
label: 'Work Policy',
|
||||
defaultValue: ['WORK_POLICY_1', 'WORK_POLICY_2'],
|
||||
options: [
|
||||
{
|
||||
label: 'Work Policy 1',
|
||||
value: 'WORK_POLICY_1',
|
||||
color: 'blue',
|
||||
},
|
||||
],
|
||||
onPersist: fn(),
|
||||
VariablePicker: () => <div>VariablePicker</div>,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const firstChip = await canvas.findByText('Work Policy 1');
|
||||
expect(firstChip).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
label: 'Work Policy',
|
||||
defaultValue: ['WORK_POLICY_1', 'WORK_POLICY_2'],
|
||||
options: [
|
||||
{
|
||||
label: 'Work Policy 1',
|
||||
value: 'WORK_POLICY_1',
|
||||
color: 'blue',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 2',
|
||||
value: 'WORK_POLICY_2',
|
||||
color: 'green',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 3',
|
||||
value: 'WORK_POLICY_3',
|
||||
color: 'red',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 4',
|
||||
value: 'WORK_POLICY_4',
|
||||
color: 'yellow',
|
||||
},
|
||||
],
|
||||
onPersist: fn(),
|
||||
readonly: true,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const firstChip = await canvas.findByText('Work Policy 1');
|
||||
expect(firstChip).toBeVisible();
|
||||
|
||||
await userEvent.click(firstChip);
|
||||
|
||||
const searchInputInModal = canvas.queryByPlaceholderText('Search');
|
||||
expect(searchInputInModal).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
export const DisabledWithVariable: Story = {
|
||||
args: {
|
||||
label: 'Created At',
|
||||
defaultValue: `{{a.b.c}}`,
|
||||
onPersist: fn(),
|
||||
readonly: true,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const variableChip = await canvas.findByText('c');
|
||||
expect(variableChip).toBeVisible();
|
||||
|
||||
await userEvent.click(variableChip);
|
||||
|
||||
const searchInputInModal = canvas.queryByPlaceholderText('Search');
|
||||
expect(searchInputInModal).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { expect } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { within } from '@storybook/test';
|
||||
import { FormNumberFieldInput } from '../FormNumberFieldInput';
|
||||
@ -36,3 +37,36 @@ export const WithLabel: Story = {
|
||||
await canvas.findByPlaceholderText('Number field...');
|
||||
},
|
||||
};
|
||||
|
||||
export const WithVariablePicker: Story = {
|
||||
args: {
|
||||
placeholder: 'Number field...',
|
||||
VariablePicker: () => <div>VariablePicker</div>,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const variablePicker = await canvas.findByText('VariablePicker');
|
||||
|
||||
expect(variablePicker).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
placeholder: 'Number field...',
|
||||
readonly: true,
|
||||
VariablePicker: () => <div>VariablePicker</div>,
|
||||
defaultValue: 123,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const input = await canvas.findByDisplayValue('123');
|
||||
|
||||
expect(input).toBeDisabled();
|
||||
|
||||
const variablePicker = canvas.queryByText('VariablePicker');
|
||||
expect(variablePicker).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
@ -0,0 +1,157 @@
|
||||
import { expect } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { fn, userEvent, within } from '@storybook/test';
|
||||
import { FormSelectFieldInput } from '../FormSelectFieldInput';
|
||||
|
||||
const meta: Meta<typeof FormSelectFieldInput> = {
|
||||
title: 'UI/Data/Field/Form/Input/FormSelectFieldInput',
|
||||
component: FormSelectFieldInput,
|
||||
args: {},
|
||||
argTypes: {},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof FormSelectFieldInput>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
label: 'Work Policy',
|
||||
defaultValue: 'WORK_POLICY_1',
|
||||
options: [
|
||||
{
|
||||
label: 'Work Policy 1',
|
||||
value: 'WORK_POLICY_1',
|
||||
color: 'blue',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 2',
|
||||
value: 'WORK_POLICY_2',
|
||||
color: 'green',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 3',
|
||||
value: 'WORK_POLICY_3',
|
||||
color: 'red',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 4',
|
||||
value: 'WORK_POLICY_4',
|
||||
color: 'yellow',
|
||||
},
|
||||
],
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const selectedOption = await canvas.findByText('Work Policy');
|
||||
|
||||
expect(selectedOption).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
export const WithVariablePicker: Story = {
|
||||
args: {
|
||||
label: 'Work Policy',
|
||||
defaultValue: 'WORK_POLICY_1',
|
||||
options: [
|
||||
{
|
||||
label: 'Work Policy 1',
|
||||
value: 'WORK_POLICY_1',
|
||||
color: 'blue',
|
||||
},
|
||||
],
|
||||
onPersist: fn(),
|
||||
VariablePicker: () => <div>VariablePicker</div>,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const firstChip = await canvas.findByText('Work Policy 1');
|
||||
expect(firstChip).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
label: 'Work Policy',
|
||||
defaultValue: 'WORK_POLICY_1',
|
||||
options: [
|
||||
{
|
||||
label: 'Work Policy 1',
|
||||
value: 'WORK_POLICY_1',
|
||||
color: 'blue',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 2',
|
||||
value: 'WORK_POLICY_2',
|
||||
color: 'green',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 3',
|
||||
value: 'WORK_POLICY_3',
|
||||
color: 'red',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 4',
|
||||
value: 'WORK_POLICY_4',
|
||||
color: 'yellow',
|
||||
},
|
||||
],
|
||||
onPersist: fn(),
|
||||
readonly: true,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const firstChip = await canvas.findByText('Work Policy 1');
|
||||
expect(firstChip).toBeVisible();
|
||||
|
||||
await userEvent.click(firstChip);
|
||||
|
||||
const searchInputInModal = canvas.queryByPlaceholderText('Search');
|
||||
expect(searchInputInModal).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
export const DisabledWithVariable: Story = {
|
||||
args: {
|
||||
label: 'Created At',
|
||||
defaultValue: `{{a.b.c}}`,
|
||||
options: [
|
||||
{
|
||||
label: 'Work Policy 1',
|
||||
value: 'WORK_POLICY_1',
|
||||
color: 'blue',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 2',
|
||||
value: 'WORK_POLICY_2',
|
||||
color: 'green',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 3',
|
||||
value: 'WORK_POLICY_3',
|
||||
color: 'red',
|
||||
},
|
||||
{
|
||||
label: 'Work Policy 4',
|
||||
value: 'WORK_POLICY_4',
|
||||
color: 'yellow',
|
||||
},
|
||||
],
|
||||
onPersist: fn(),
|
||||
readonly: true,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const variableChip = await canvas.findByText('c');
|
||||
expect(variableChip).toBeVisible();
|
||||
|
||||
await userEvent.click(variableChip);
|
||||
|
||||
const searchInputInModal = canvas.queryByPlaceholderText('Search');
|
||||
expect(searchInputInModal).not.toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
@ -1,5 +1,5 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { within } from '@storybook/test';
|
||||
import { expect, fn, userEvent, within } from '@storybook/test';
|
||||
import { FormTextFieldInput } from '../FormTextFieldInput';
|
||||
|
||||
const meta: Meta<typeof FormTextFieldInput> = {
|
||||
@ -43,3 +43,47 @@ export const Multiline: Story = {
|
||||
await canvas.findByText(/^Text$/);
|
||||
},
|
||||
};
|
||||
|
||||
export const WithVariablePicker: Story = {
|
||||
args: {
|
||||
label: 'Text',
|
||||
placeholder: 'Text field...',
|
||||
VariablePicker: () => <div>VariablePicker</div>,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const variablePicker = await canvas.findByText('VariablePicker');
|
||||
|
||||
expect(variablePicker).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
label: 'Text',
|
||||
placeholder: 'Text field...',
|
||||
defaultValue: 'Text field',
|
||||
readonly: true,
|
||||
VariablePicker: () => <div>VariablePicker</div>,
|
||||
onPersist: fn(),
|
||||
},
|
||||
play: async ({ canvasElement, args }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const variablePicker = canvas.queryByText('VariablePicker');
|
||||
expect(variablePicker).not.toBeInTheDocument();
|
||||
|
||||
const editor = canvasElement.querySelector('.ProseMirror > p');
|
||||
expect(editor).toBeVisible();
|
||||
|
||||
const defaultValue = await canvas.findByText('Text field');
|
||||
expect(defaultValue).toBeVisible();
|
||||
|
||||
await userEvent.type(editor, 'Hello');
|
||||
|
||||
expect(args.onPersist).not.toHaveBeenCalled();
|
||||
expect(canvas.queryByText('Hello')).not.toBeInTheDocument();
|
||||
expect(defaultValue).toBeVisible();
|
||||
},
|
||||
};
|
||||
|
||||
@ -194,7 +194,9 @@ export const ReplaceStaticValueWithVariable: Story = {
|
||||
}),
|
||||
]);
|
||||
|
||||
const removeVariableButton = await canvas.findByTestId(/^remove-icon/);
|
||||
const removeVariableButton = canvasElement.querySelector(
|
||||
'button .tabler-icon-x',
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
userEvent.click(removeVariableButton),
|
||||
|
||||
@ -4,22 +4,8 @@ type SelectDisplayProps = {
|
||||
color: ThemeColor | 'transparent';
|
||||
label: string;
|
||||
Icon?: IconComponent;
|
||||
isUsedInForm?: boolean;
|
||||
};
|
||||
|
||||
export const SelectDisplay = ({
|
||||
color,
|
||||
label,
|
||||
Icon,
|
||||
isUsedInForm,
|
||||
}: SelectDisplayProps) => {
|
||||
return (
|
||||
<Tag
|
||||
preventShrink
|
||||
color={color}
|
||||
text={label}
|
||||
Icon={Icon}
|
||||
preventPadding={isUsedInForm}
|
||||
/>
|
||||
);
|
||||
export const SelectDisplay = ({ color, label, Icon }: SelectDisplayProps) => {
|
||||
return <Tag preventShrink color={color} text={label} Icon={Icon} />;
|
||||
};
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { BooleanDisplay } from '@/ui/field/display/components/BooleanDisplay';
|
||||
|
||||
const StyledEditableBooleanFieldContainer = styled.div`
|
||||
const StyledEditableBooleanFieldContainer = styled.div<{ readonly?: boolean }>`
|
||||
align-items: center;
|
||||
cursor: ${({ onClick }) => (onClick ? 'pointer' : 'default')};
|
||||
display: flex;
|
||||
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
color: ${({ theme, readonly }) =>
|
||||
readonly ? theme.font.color.tertiary : theme.font.color.primary};
|
||||
`;
|
||||
|
||||
type BooleanInputProps = {
|
||||
@ -39,6 +42,7 @@ export const BooleanInput = ({
|
||||
return (
|
||||
<StyledEditableBooleanFieldContainer
|
||||
onClick={readonly ? undefined : handleClick}
|
||||
readonly={readonly}
|
||||
data-testid={testId}
|
||||
>
|
||||
<BooleanDisplay value={internalValue} />
|
||||
|
||||
@ -9,6 +9,10 @@ export const StyledTextInput = styled.input`
|
||||
margin: 0;
|
||||
${TEXT_INPUT_STYLE}
|
||||
width: 100%;
|
||||
|
||||
&:disabled {
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
}
|
||||
`;
|
||||
|
||||
type TextInputProps = {
|
||||
@ -25,6 +29,7 @@ type TextInputProps = {
|
||||
onChange?: (newText: string) => void;
|
||||
copyButton?: boolean;
|
||||
shouldTrim?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const getValue = (value: string, shouldTrim: boolean) => {
|
||||
@ -49,6 +54,7 @@ export const TextInput = ({
|
||||
onChange,
|
||||
copyButton = true,
|
||||
shouldTrim = true,
|
||||
disabled,
|
||||
}: TextInputProps) => {
|
||||
const [internalText, setInternalText] = useState(value);
|
||||
|
||||
@ -85,6 +91,7 @@ export const TextInput = ({
|
||||
onChange={handleChange}
|
||||
autoFocus={autoFocus}
|
||||
value={internalText}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{copyButton && (
|
||||
<div ref={copyRef}>
|
||||
|
||||
@ -215,6 +215,7 @@ export const WorkflowEditActionFormCreateRecord = ({
|
||||
handleFieldChange(field.metadata.fieldName, value);
|
||||
}}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
readonly={isFormDisabled}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@ -248,6 +248,7 @@ export const WorkflowEditActionFormUpdateRecord = ({
|
||||
handleFieldChange(fieldDefinition.metadata.fieldName, value);
|
||||
}}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
readonly={isFormDisabled}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user