Skip to content

Commit

Permalink
Merge pull request #636 from gadget-inc/mill/AddLabelPropToAutoInput
Browse files Browse the repository at this point in the history
Updated `AutoInput` to accept an optional `label?: string` input
  • Loading branch information
MillanWangGadget committed Sep 12, 2024
2 parents 981af30 + ea9ded4 commit 0b52c96
Show file tree
Hide file tree
Showing 27 changed files with 188 additions and 61 deletions.
5 changes: 5 additions & 0 deletions packages/react/.changeset/clean-flies-provide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@gadgetinc/react": patch
---

Updated `AutoInput` to accept an optional `label?: string` input
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* eslint-disable jest/valid-expect */
import React from "react";
import { MUIAutoInput } from "../../../../src/auto/mui/inputs/MUIAutoInput.js";
import { PolarisAutoInput } from "../../../../src/auto/polaris/inputs/PolarisAutoInput.js";
import { humanizeCamelCase } from "../../../../src/utils.js";
import { api } from "../../../support/api.js";
import { describeForEachAutoAdapter } from "../../../support/auto.js";

const AutoInput = (props: { suiteName: string; field: string; label?: string }) => {
if (props.suiteName === "Polaris") {
return <PolarisAutoInput {...props} />;
}

if (props.suiteName === "MUI") {
return <MUIAutoInput {...props} />;
}

throw new Error("Invalid suite name");
};

const widgetFieldApiIds = [
"name",
"inventoryCount",
"gizmos",
"anything",
"description",
"category",
"startsAt",
"isChecked",
"metafields",
"roles",
"secretKey",
"section",
"mustBeLongString",
];

describeForEachAutoAdapter("AutoForm - input labels", ({ name, adapter: { AutoForm }, wrapper }) => {
beforeEach(() => {
cy.viewport("macbook-13");
});

it("renders AutoInputs with custom labels", () => {
cy.mountWithWrapper(
<AutoForm action={api.widget.create}>
{widgetFieldApiIds.map((fieldApiId) => (
<AutoInput suiteName={name} field={fieldApiId} label={fieldApiId + "_CustomLabel"} key={fieldApiId} />
))}
</AutoForm>,
wrapper
);

for (const fieldApiId of widgetFieldApiIds) {
cy.contains(fieldApiId + "_CustomLabel").should("exist");
}
});

it("renders AutoInputs with default labels", () => {
cy.mountWithWrapper(
<AutoForm action={api.widget.create}>
{widgetFieldApiIds.map((fieldApiId) => (
<AutoInput suiteName={name} field={fieldApiId} key={fieldApiId} />
))}
</AutoForm>,
wrapper
);

for (const fieldName of widgetFieldNames) {
cy.contains(humanizeCamelCase(fieldName)).should("exist");
}
});
});

const widgetFieldNames = [
"Name",
"Inventory count",
"Gizmos",
"Anything",
"Description",
"Category",
"Starts at",
"Is checked",
"Metafields",
"Roles",
"Secret key",
"Section",
"Must be long string",
];
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface AutoRelationshipInputProps {
field: string;
control?: Control<any>;
optionLabel?: OptionLabel;
label?: string;
}

/**
Expand Down
9 changes: 8 additions & 1 deletion packages/react/src/auto/mui/MUIAutoForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,19 @@ export const MUIAutoFormComponent = <
return props.successContent;
}

if (fetchingMetadata) {
return (
<Grid container component="form" spacing={2} onSubmit={submit as any} {...rest}>
<MUIFormSkeleton />
</Grid>
);
}

const formContent = props.children ?? (
<>
{formTitle && <Typography variant="h5">{formTitle}</Typography>}
{!props.onSuccess && <MUISubmitSuccessfulBanner />}
{!props.onFailure && <MUISubmitErrorBanner />}
{fetchingMetadata && <MUIFormSkeleton />}
{!metadataError && (
<>
{fields.map(({ metadata }) => (
Expand Down
6 changes: 3 additions & 3 deletions packages/react/src/auto/mui/inputs/MUIAutoBooleanInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { useController, type Control } from "react-hook-form";
import { useFieldMetadata } from "../../hooks/useFieldMetadata.js";
import { MUIAutoFormControl } from "./MUIAutoFormControl.js";

export const MUIAutoBooleanInput = (props: { field: string; control?: Control<any> } & Partial<CheckboxProps>) => {
const { field: fieldApiIdentifier, control, ...rest } = props;
export const MUIAutoBooleanInput = (props: { field: string; control?: Control<any>; label?: string } & Partial<CheckboxProps>) => {
const { field: fieldApiIdentifier, label, control, ...rest } = props;

const { path } = useFieldMetadata(fieldApiIdentifier);

Expand All @@ -18,7 +18,7 @@ export const MUIAutoBooleanInput = (props: { field: string; control?: Control<an
const { value: _value, ...restFieldProps } = fieldProps;

return (
<MUIAutoFormControl field={props.field}>
<MUIAutoFormControl field={props.field} label={label}>
<Checkbox {...restFieldProps} checked={fieldProps.value} {...rest} />
</MUIAutoFormControl>
);
Expand Down
10 changes: 8 additions & 2 deletions packages/react/src/auto/mui/inputs/MUIAutoDateTimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@ import { zonedTimeToUtc } from "../../../dateTimeUtils.js";
import type { GadgetDateTimeConfig } from "../../../internal/gql/graphql.js";
import { useFieldMetadata } from "../../hooks/useFieldMetadata.js";

export const MUIAutoDateTimePicker = (props: { field: string; value?: Date; onChange?: (value: Date) => void; error?: string }) => {
export const MUIAutoDateTimePicker = (props: {
field: string;
value?: Date;
onChange?: (value: Date) => void;
error?: string;
label?: string;
}) => {
const localTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
const { path, metadata } = useFieldMetadata(props.field);
const config = metadata.configuration;
Expand All @@ -17,7 +23,7 @@ export const MUIAutoDateTimePicker = (props: { field: string; value?: Date; onCh
return (
<Box sx={{ display: "flex" }}>
<DatePicker
label={metadata.name}
label={props.label ?? metadata.name}
onChange={(newValue: string | number | Date | null) => {
props.onChange?.(zonedTimeToUtc(new Date(newValue ?? ""), localTz));
fieldProps.onChange(zonedTimeToUtc(new Date(newValue ?? ""), localTz));
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/auto/mui/inputs/MUIAutoEnumInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const MUIAutoEnumInput = <
FreeSolo extends boolean | undefined = false,
ChipComponent extends React.ElementType = ChipTypeMap["defaultComponent"]
>(
props: { field: string } & Partial<AutocompleteProps<Value, Multiple, DisableClearable, FreeSolo, ChipComponent>>
props: { field: string; label?: string } & Partial<AutocompleteProps<Value, Multiple, DisableClearable, FreeSolo, ChipComponent>>
) => {
const { allowMultiple, selectedOptions, onSelectionChange, allOptions, label } = useEnumInputController(props);

Expand All @@ -19,7 +19,7 @@ export const MUIAutoEnumInput = <
disablePortal
multiple={allowMultiple}
options={allOptions}
renderInput={(params) => <TextField {...params} label={label} />}
renderInput={(params) => <TextField {...params} label={props.label ?? label} />}
value={allowMultiple ? selectedOptions : selectedOptions[0]}
onChange={(event, value) => {
if (value === null || (Array.isArray(value) && value.length === 0)) {
Expand Down
6 changes: 3 additions & 3 deletions packages/react/src/auto/mui/inputs/MUIAutoFileInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ const VisuallyHiddenInput = styled("input")({
width: 1,
});

export const MUIAutoFileInput = (props: { field: string; control?: Control<any> }) => {
const { field: fieldApiIdentifier, control } = props;
export const MUIAutoFileInput = (props: { field: string; control?: Control<any>; label?: string }) => {
const { field: fieldApiIdentifier, control, label } = props;
const { onFileUpload, metadata } = useFileInputController({
field: fieldApiIdentifier,
control,
Expand All @@ -31,7 +31,7 @@ export const MUIAutoFileInput = (props: { field: string; control?: Control<any>
return (
<MUIAutoFormControl field={props.field}>
<Button component="label" variant="contained">
{metadata.name} {metadata.requiredArgumentForInput ? "*" : null}
{props.label ?? metadata.name} {metadata.requiredArgumentForInput ? "*" : null}
<VisuallyHiddenInput
type="file"
onChange={(event) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/auto/mui/inputs/MUIAutoFormControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from "react";
import { useController } from "react-hook-form";
import { useFieldMetadata } from "../../hooks/useFieldMetadata.js";

export const MUIAutoFormControl = (props: { field: string; children: ReactElement }) => {
export const MUIAutoFormControl = (props: { field: string; children: ReactElement; label?: string }) => {
const { path, metadata } = useFieldMetadata(props.field);
const {
fieldState: { error },
Expand All @@ -15,7 +15,7 @@ export const MUIAutoFormControl = (props: { field: string; children: ReactElemen
return (
<FormControl {...metadata} error={!!error}>
<FormGroup>
<FormControlLabel label={metadata.name} control={props.children} />
<FormControlLabel label={props.label ?? metadata.name} control={props.children} />
</FormGroup>
{error && <FormHelperText>{error?.message}</FormHelperText>}
</FormControl>
Expand Down
30 changes: 15 additions & 15 deletions packages/react/src/auto/mui/inputs/MUIAutoInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,62 +17,62 @@ import { MUIAutoHasManyInput } from "./relationships/MUIAutoHasManyInput.js";
// lazy import for smaller bundle size by default
const MUIAutoRichTextInput = React.lazy(() => import("./MUIAutoRichTextInput.js"));

export const MUIAutoInput = (props: { field: string }) => {
export const MUIAutoInput = (props: { field: string; label?: string }) => {
const { metadata } = useFieldMetadata(props.field);
const config = metadata.configuration;

switch (config.fieldType) {
case FieldType.Id: {
return <MUIAutoIdInput field={props.field} />;
return <MUIAutoIdInput field={props.field} label={props.label} />;
}
case FieldType.String:
case FieldType.Number:
case FieldType.Email:
case FieldType.Color:
case FieldType.Url: {
return <MUIAutoTextInput field={props.field} />;
return <MUIAutoTextInput field={props.field} label={props.label} />;
}
case FieldType.EncryptedString: {
return <MUIAutoEncryptedStringInput field={props.field} />;
return <MUIAutoEncryptedStringInput field={props.field} label={props.label} />;
}
case FieldType.Password: {
return <MUIAutoPasswordInput field={props.field} />;
return <MUIAutoPasswordInput field={props.field} label={props.label} />;
}
case FieldType.Boolean: {
return <MUIAutoBooleanInput field={props.field} />;
return <MUIAutoBooleanInput field={props.field} label={props.label} />;
}
case FieldType.DateTime: {
return <MUIAutoDateTimePicker field={props.field} />;
return <MUIAutoDateTimePicker field={props.field} label={props.label} />;
}
case FieldType.Json: {
return <MUIAutoJSONInput field={props.field} />;
return <MUIAutoJSONInput field={props.field} label={props.label} />;
}
case FieldType.Enum: {
return <MUIAutoEnumInput field={props.field} />;
return <MUIAutoEnumInput field={props.field} label={props.label} />;
}
case FieldType.File: {
return <MUIAutoFileInput field={props.field} />;
return <MUIAutoFileInput field={props.field} label={props.label} />;
}
case FieldType.RoleAssignments: {
return <MUIAutoRolesInput field={props.field} />;
return <MUIAutoRolesInput field={props.field} label={props.label} />;
}
case FieldType.BelongsTo: {
return <MUIAutoBelongsToInput field={props.field} />;
return <MUIAutoBelongsToInput field={props.field} label={props.label} />;
}
case FieldType.HasOne: {
// TODO - Update implementation of MUIAutoHasOneInput after 1-1 mapping maintenance system is updated in API
// return <MUIAutoHasOneInput field={props.field} />;
// return <MUIAutoHasOneInput field={props.field} label={props.label} />;
return null;
}
case FieldType.HasMany: {
return <MUIAutoHasManyInput field={props.field} />;
return <MUIAutoHasManyInput field={props.field} label={props.label} />;
}
case FieldType.HasManyThrough: {
// TODO: implement HasManyThrough input with join model record create/delete/update
return null;
}
case FieldType.RichText: {
return <MUIAutoRichTextInput field={props.field} />;
return <MUIAutoRichTextInput field={props.field} label={props.label} />;
}
case FieldType.Money: {
// TODO: implement money input
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/auto/mui/inputs/MUIAutoJSONInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const MUIAutoJSONInput = (
const { type: _type, errorMessage, ...controller } = useJSONInputController(props);

const inErrorState = !isFocused && !!errorMessage;

const label = props.label ?? controller.label;
return (
<TextField
multiline
Expand All @@ -27,6 +27,7 @@ export const MUIAutoJSONInput = (
{...controller}
{...focusProps}
{...restOfProps}
label={label}
onChange={(event) => controller.onChange(event.target.value)}
/>
);
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/auto/mui/inputs/MUIAutoRolesInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const MUIAutoRolesInput = (
props: {
field: string; // Field API identifier
control?: Control<any>;
label?: string;
} & Partial<AutocompleteProps<{ id: string; label: string }, true, any, any>>
) => {
const { options, loading, rolesError, fieldError, selectedRoleKeys, fieldProps, metadata } = useRoleInputController(props);
Expand All @@ -19,7 +20,7 @@ export const MUIAutoRolesInput = (
throw fieldError;
}

const label = metadata.name;
const label = props.label ?? metadata.name;
if (loading) {
return <TextField label={label} autoComplete="off" disabled={loading} />;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/auto/mui/inputs/MUIAutoTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ export const MUIAutoTextInput = (
const stringInputController = useStringInputController({ field, control });

const isRequired = stringInputController.metadata.requiredArgumentForInput;
const label = stringInputController.label + (isRequired ? " *" : "");
const label = (props.label ?? stringInputController.label) + (isRequired ? " *" : "");
return (
<TextField
{...stringInputController}
label={label}
error={stringInputController.isError}
helperText={stringInputController.errorMessage}
{...props}
label={label}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@ export const MUIAutoBelongsToInput = (props: AutoRelationshipInputProps) => {
onChange={(e, selectedValue) => onSelectRecord(selectedValue.id)}
onClose={() => search.set()}
renderInput={(params) => (
<TextField {...params} value={search.value} label={metadata.name} onChange={(e) => search.set(e.target.value)} name={path} />
<TextField
{...params}
value={search.value}
label={props.label ?? metadata.name}
onChange={(e) => search.set(e.target.value)}
name={path}
/>
)}
></Autocomplete>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ export const MUIAutoHasManyInput = (props: AutoRelationshipInputProps) => {
onChange={(e, selectedValue) => selectedValue.forEach((id) => onSelectRecord(id))}
onClose={() => search.set()}
renderInput={(params) => (
<TextField {...params} value={search.value} label={metadata.name} onChange={(e) => search.set(e.target.value)} name={path} />
<TextField
{...params}
value={search.value}
label={props.label ?? metadata.name}
onChange={(e) => search.set(e.target.value)}
name={path}
/>
)}
></Autocomplete>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export const PolarisAutoBooleanInput = (props: { field: string; control?: Contro
name: path,
});

const label = props.label ?? metadata.name;
const { value: _value, ...restFieldProps } = fieldProps;

return <Checkbox label={metadata.name} {...restFieldProps} checked={!!fieldProps.value} error={error?.message} {...rest} />;
return <Checkbox {...restFieldProps} checked={!!fieldProps.value} error={error?.message} {...rest} label={label} />;
};
Loading

0 comments on commit 0b52c96

Please sign in to comment.