Skip to content

Conversation

@MohnDoe
Copy link

@MohnDoe MohnDoe commented Nov 2, 2025

Closes #78 Adding missing ReferenceOneField component

TODO before submitting PR :

  • Basic story in Storybook
  • Add label prop (not needed! After some digging it looks like Shadcn RA does not use label on the ReferenceX components but gets its label from the child RecordField component)
  • Documentation
  • More stories
    • Basic
    • Loading / Slow network
    • Using render prop
    • In a Show layout
    • Using a string as empty prop
    • Using translation as empty prop value
    • Offline netflow

@fzaninotto
Copy link
Member

Excellent, looking forward to seeing this complete!

@MohnDoe MohnDoe marked this pull request as ready for review November 4, 2025 15:09
Copy link
Collaborator

@slax57 slax57 left a comment

Choose a reason for hiding this comment

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

Sorry we took so long to review.

There are a few minor issues, and a small confusion about what component should be responsible of rendering the label, but apart from that this looks very promising!

Thank you for your contribution!

Feel free to ask if you have further questions.

<DateField source="published_at" />
<ReferenceField source="authorId" reference="authors" />
<ReferenceOneField reference="book_details" target="book_id">
<RecordField label="Genre" source="genre" />
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why use RecordField and not, say, TextField here? 🤔
This is contradictory to what this doc says at line 20.

UPDATE: reading the whole PR and notably the stories, I realize you may be confused about what component is responsible to render the label.

  • RecordField should be used only for cases where you are not a direct children of a component responsible of rendering the labels, like SimpleShowLayout for instance.
  • Using ReferenceOneField as direct child of a SimpleShowLayout should render a label on top of the field, derived either from the source or from the label prop if one is provided.
  • Usually, ReferenceOneField will be used to render a single child, like a TextField, that's why it doesn't need to render its child component's label too.
  • However if you choose to render several children fields in a ReferenceOneField, then yes it makes sense to use RecordField here. And you will need to hide the label of the ReferenceOneField by either not rendering it in a SimpleShowLayout or by passing label={false} to it.

I hope it makes sense. Feel free to ask further questions if still not clear.

Comment on lines +51 to +64
| Prop | Required | Type | Default | Description |
|------|----------|------|---------|-------------|
| `source` | Required | `string` | - | Foreign key in current record |
| `reference` | Required | `string` | - | Target resource name |
| `target` | Required | `string` | - | Target field carrying the relationship on the referenced resource, e.g. `book_id` |
| `children` | Optional | `ReactNode` | `<span>` representation | Custom child (can use context hooks) |
| `empty` | Optional | `ReactNode` | - | Placeholder when no id / value |
| `error` | Optional | `ReactNode` | - | Error element (set `false` to hide) |
| `loading` | Optional | `ReactNode` | - | Element while loading (set `false` to hide) |
| `queryOptions` | Optional | `UseQueryOptions` | - | TanStack Query options (meta, staleTime, etc.) |
| `record` | Optional | `object` | Context record | Explicit record |
| `render` | Optional | `(ctx)=>ReactNode` | - | Custom renderer receiving reference field context |
| `link` | Optional | `LinkToType` | `edit` | Link target or false / function |
| `offline` | Optional | `ReactNode` | - | The text or element to display when there is no network connectivity |
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems you forgot about the filter and sort props. Also, can you order the Optional props alphabetically?

Comment on lines +66 to +72
## Record Representation

[See `ReferenceField`](./ReferenceField.md#record-representation)

## Tips

[See `ReferenceField`](./ReferenceField.md#tips) No newline at end of file
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you copy these sections instead of referencing them?

Comment on lines +125 to +135
export const InShowLayout = () => (
<Wrapper dataProvider={dataProvider}>
<div className="flex flex-col gap-4">
<TextField source="name" />
<ReferenceOneField reference="workoutDetails" source="short_id" target="workout_id">
<RecordField source="note" label="Workout note" />
<RecordField source="duration" label="Duration" />
</ReferenceOneField>
</div>
</Wrapper>
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This story should use SimpleShowLayout

Comment on lines +8 to +16
it('should render its child in the context of the related record', async () => {
const screen = render(<Basic />);
await expect.element(screen.getByText(EXPECTED_WORKOUT_NOTE)).toBeInTheDocument();
});

it('should allow to render the referenceRecord using a render prop', async () => {
const screen = render(<WithRenderProp />);
await expect.element(screen.getByText(EXPECTED_WORKOUT_NOTE)).toBeInTheDocument();
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you also add a test to cover the case where children are passed?

"recordfield",
"referencearrayfield",
"referencefield",
"referenceonefield",
Copy link
Collaborator

Choose a reason for hiding this comment

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

please sort by alphabetical order

Comment on lines +107 to +123
export const WithRenderProp = () => (
<Wrapper dataProvider={slowDataProvider}>
<ReferenceOneField
reference="workoutDetails"
source="short_id"
target="workout_id"
render={({ isPending, error, referenceRecord }) => {
if (isPending) {
return <p>Loading...</p>;
}
if (error) {
return <p style={{ color: 'red' }}>{error.toString()}</p>
}
return (<span>{referenceRecord ? referenceRecord.note : <b>No note.</b>}</span>)
}} />
</Wrapper>
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I never see the 'Loading...' message with this story. Can you please check the implementation?

@fzaninotto
Copy link
Member

Hi, we took the time to review your patch. Can you please amend it to conform with our comments? Otherwise we'll have to close the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ReferenceOneField component

3 participants