Skip to content

Commit

Permalink
Merge branch 'master' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
fzaninotto committed Apr 24, 2021
2 parents 78f79b3 + 395dd12 commit dd0e793
Show file tree
Hide file tree
Showing 64 changed files with 680 additions and 279 deletions.
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
# Changelog

## v3.14.5

* Fix `<DateIpnut>` and `<DateTimeInput>` are broken on Safari ([6199](https://github.com/marmelab/react-admin/pull/6199)) ([djhi](https://github.com/djhi))
* Fix `<Notification>` undo button's color on success type ([6193](https://github.com/marmelab/react-admin/pull/6193)) ([WiXSL](https://github.com/WiXSL))
* [TypeScript] Publish `data-generator typings` ([6204](https://github.com/marmelab/react-admin/pull/6204)) ([floo51](https://github.com/floo51))
* [TypeScript] Fix `ra-data-local-storage` types ([6203](https://github.com/marmelab/react-admin/pull/6203)) ([djhi](https://github.com/djhi))
* [TypeScript] Fix view action component types aren't exported ([6200](https://github.com/marmelab/react-admin/pull/6200)) ([djhi](https://github.com/djhi))
* [TypeScript] Fix sidebar width type in application theme ([6197](https://github.com/marmelab/react-admin/pull/6197)) ([jtomaszewski](https://github.com/jtomaszewski))
* [Doc] Add OData data provider ([6206](https://github.com/marmelab/react-admin/pull/6206)) ([jvert](https://github.com/jvert))
* [Doc] Update tutorial images ([6205](https://github.com/marmelab/react-admin/pull/6205)) ([fzaninotto](https://github.com/fzaninotto))
* [Doc] Fix custom fields documentation doesn't use `useRecordContext` ([6201](https://github.com/marmelab/react-admin/pull/6201)) ([djhi](https://github.com/djhi))

## v3.14.4

* Fix `useGetMany` does not respect the `enabled` option ([6188](https://github.com/marmelab/react-admin/pull/6188)) ([djhi](https://github.com/djhi))
* Fix 'Cannot set property validating of undefined' error when conditionally rendering a form component ([6186](https://github.com/marmelab/react-admin/pull/6186)) ([ThieryMichel](https://github.com/ThieryMichel))
* Fix `useWarnWhenUsavedChanges` fails on nested fields ([6185](https://github.com/marmelab/react-admin/pull/6185)) ([djhi](https://github.com/djhi))
* Fix warning when using `<BulkDeleteButton>` without props ([6165](https://github.com/marmelab/react-admin/pull/6165)) ([fzaninotto](https://github.com/fzaninotto))
* Fix Menu icon isn't aligned with the sidebar icons ([6161](https://github.com/marmelab/react-admin/pull/6161)) ([JayKaku](https://github.com/JayKaku))
* Fix missing query string after successful login ([6129](https://github.com/marmelab/react-admin/pull/6129)) ([makbol](https://github.com/makbol))
* [Doc] Add link to Google Sheet data provider ([6187](https://github.com/marmelab/react-admin/pull/6187)) ([fzaninotto](https://github.com/fzaninotto))
* [Doc] Fix missing documentation about the ResourceContext ([6183](https://github.com/marmelab/react-admin/pull/6183)) ([fzaninotto](https://github.com/fzaninotto))
* [Doc] Fix broken link to source in Testing Permissions documentation ([6181](https://github.com/marmelab/react-admin/pull/6181)) ([YashJipkate](https://github.com/YashJipkate))
* [Doc] Fix typo in `<FormDataConsumer>` usage JSDoc ([6169](https://github.com/marmelab/react-admin/pull/6169)) ([WiXSL](https://github.com/WiXSL))
* [Doc] Fix typo in `withDataProvider` hook example ([6160](https://github.com/marmelab/react-admin/pull/6160)) ([f-jost](https://github.com/f-jost))
* [Doc] Fix outdated link for Swedish translation ([6156](https://github.com/marmelab/react-admin/pull/6156)) ([kolben](https://github.com/kolben))

## v3.14.3

* Fix `<Field textAlign>` prop doesn't accept value `center` ([6152](https://github.com/marmelab/react-admin/pull/6152)) ([WiXSL](https://github.com/WiXSL))
* Fix runtime warnings when `<SimpleList>` displays skeleton while loading ([6146](https://github.com/marmelab/react-admin/pull/6146)) ([fzaninotto](https://github.com/fzaninotto))
* Fix `useRedirect` does not handle query strings ([6145](https://github.com/marmelab/react-admin/pull/6145)) ([fzaninotto](https://github.com/fzaninotto))
* Fix logout notification may appear more than once ([6144](https://github.com/marmelab/react-admin/pull/6144)) ([fzaninotto](https://github.com/fzaninotto))
* Fix submit errors cannot have translation arguments ([6140](https://github.com/marmelab/react-admin/pull/6140)) ([djhi](https://github.com/djhi))
* Fix `<RadioButtonGroupInput>` emits runtime warnings ([6139](https://github.com/marmelab/react-admin/pull/6139)) ([djhi](https://github.com/djhi))
Expand Down
17 changes: 17 additions & 0 deletions cypress/integration/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,21 @@ describe('Authentication', () => {
ListPage.navigate();
cy.url().then(url => expect(url).to.contain('/#/posts'));
});

it('should redirect to initial url keeping query string', () => {
let urlBeforeLogout;

ListPage.navigate();
ListPage.addCommentableFilter();
cy.url().then(url => {
urlBeforeLogout = url;
});
ListPage.setAsNonLogged();
cy.reload();
LoginPage.login('login', 'password');
cy.url().then(urlAfterLogin => {
expect(urlAfterLogin).to.contain(urlBeforeLogout);
});
ListPage.commentableFilter().should('exist');
});
});
16 changes: 16 additions & 0 deletions cypress/support/ListPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default url => ({
appLoader: '.app-loader',
displayedRecords: '.displayed-records',
filter: name => `.filter-field[data-source='${name}'] input`,
filterButton: name => `.filter-field[data-source='${name}']`,
filterMenuItems: `.new-filter-item`,
menuItems: `[role=menuitem]`,
filterMenuItem: source => `.new-filter-item[data-key="${source}"]`,
Expand Down Expand Up @@ -63,6 +64,15 @@ export default url => ({
return cy.get(this.elements.pageNumber(n)).click({ force: true });
},

addCommentableFilter() {
this.openFilters();
cy.get(this.elements.filterMenuItem('commentable')).click();
},

commentableFilter() {
return cy.get(this.elements.filterButton('commentable'));
},

setFilterValue(name, value, clearPreviousValue = true) {
cy.get(this.elements.filter(name));
if (clearPreviousValue) {
Expand All @@ -88,6 +98,12 @@ export default url => ({
cy.get(this.elements.logout).click();
},

setAsNonLogged() {
cy.window().then(win => {
win.localStorage.setItem('not_authenticated', true);
});
},

toggleSelectAll() {
cy.get(this.elements.selectAll).click();
},
Expand Down
2 changes: 1 addition & 1 deletion docs/Actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -867,7 +867,7 @@ const ApproveButton = ({ record }) => {
export default ApproveButton;
```

And here is the `<UserProfile>` component using the `withDataProvider` HOC instead of the `useProvider` hook:
And here is the `<UserProfile>` component using the `withDataProvider` HOC instead of the `useDataProvider` hook:

```diff
import { useState, useEffect } from 'react';
Expand Down
2 changes: 2 additions & 0 deletions docs/DataProviders.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Developers from the react-admin community have open-sourced Data Providers for m
* **[Feathersjs](https://www.feathersjs.com/)**: [josx/ra-data-feathers](https://github.com/josx/ra-data-feathers)
* **[Firebase Firestore](https://firebase.google.com/docs/firestore)**: [benwinding/react-admin-firebase](https://github.com/benwinding/react-admin-firebase).
* **[Firebase Realtime Database](https://firebase.google.com/docs/database)**: [aymendhaya/ra-data-firebase-client](https://github.com/aymendhaya/ra-data-firebase-client).
* **[Google Sheets](https://www.google.com/sheets/about/)**: [marmelab/ra-data-google-sheets](https://github.com/marmelab/ra-data-google-sheets)
* **[GraphQL](https://graphql.org/)**: [marmelab/ra-data-graphql](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql) (uses [Apollo](https://www.apollodata.com/))
* **[HAL](http://stateless.co/hal_specification.html)**: [b-social/ra-data-hal](https://github.com/b-social/ra-data-hal)
* **[Hasura](https://github.com/hasura/graphql-engine)**: [hasura/ra-data-hasura](https://github.com/hasura/ra-data-hasura), auto generates valid GraphQL queries based on the properties exposed by the Hasura API.
Expand All @@ -97,6 +98,7 @@ Developers from the react-admin community have open-sourced Data Providers for m
* **[Prisma](https://github.com/weakky/ra-data-prisma)**: [weakky/ra-data-prisma](https://github.com/weakky/ra-data-prisma)
* **[Prisma Version 2](https://www.prisma.io/)**: [panter/ra-data-prisma](https://github.com/panter/ra-data-prisma)
* **[ProcessMaker3](https://www.processmaker.com/)**: [ckoliber/ra-data-processmaker3](https://github.com/ckoliber/ra-data-processmaker3)
* **[OData](https://www.odata.org/)**: [Groopit/ra-data-odata-server](https://github.com/Groopit/ra-data-odata-server)
* **[OpenCRUD](https://www.opencrud.org/)**: [weakky/ra-data-opencrud](https://github.com/Weakky/ra-data-opencrud)
* **[REST-HAPI](https://github.com/JKHeadley/rest-hapi)**: [ra-data-rest-hapi](https://github.com/mkg20001/ra-data-rest-hapi)
* **[Sails.js](https://sailsjs.com/)**: [mpampin/ra-data-json-sails](https://github.com/mpampin/ra-data-json-sails)
Expand Down
182 changes: 104 additions & 78 deletions docs/Fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -746,8 +746,6 @@ TagsField.defaultProps = {

## Reference Fields



### `<ReferenceField>`

`<ReferenceField>` is useful for displaying many-to-one and one-to-one relationships. This component fetches a referenced record (using the `dataProvider.getMany()` method), and passes it to its child. A `<ReferenceField>` displays nothing on its own, it just fetches the data and expects its child to render it. Usual child components for `<ReferenceField>` are other `<Field>` components.
Expand Down Expand Up @@ -1364,6 +1362,85 @@ PriceField.defaultProps = {
```
{% endraw %}

## Writing Your Own Field Component

If you don't find what you need in the list above, you can write your own Field component. It must be a regular React component, accepting a `source` attribute and retrieving the `record` from the `RecordContext` with the `useRecordContext` hook. React-admin will set the `record` in this context based on the API response data at render time. The field component only needs to find the `source` in the `record` and display it.

For instance, here is an equivalent of react-admin's `<TextField>` component:

```jsx
import * as React from "react";
import PropTypes from 'prop-types';
import { useRecordContext } from 'react-admin';

const TextField = (props) => {
const { source } = props;
const record = useRecordContext(props);
return <span>{record[source]}</span>;
}

TextField.propTypes = {
label: PropTypes.string,
record: PropTypes.object,
source: PropTypes.string.isRequired,
};

export default TextField;
```

**Tip**: The `label` attribute isn't used in the `render()` method, but react-admin uses it to display the table header.

**Tip**: If you want to support deep field sources (e.g. source values like `author.name`), use [lodash/get](https://www.npmjs.com/package/lodash.get) to replace the simple object lookup:

```jsx
import * as React from "react";
import PropTypes from 'prop-types';
import get from 'lodash/get';
import { useRecordContext } from 'react-admin';

const TextField = (props) => {
const { source } = props;
const record = useRecordContext(props);

return <span>{get(record, source)}</span>;
}
```

If you are not looking for reusability, you can create even simpler components, with no attributes. Let's say an API returns user records with `firstName` and `lastName` properties, and that you want to display a full name in a user list.

```js
{
id: 123,
firstName: 'John',
lastName: 'Doe'
}
```

The component will be:

```jsx
import * as React from "react";
import { List, Datagrid, TextField, useRecordContext } from 'react-admin';

const FullNameField = () => {
const record = useRecordContext(props);

return <span>{record.firstName} {record.lastName}</span>;
}

FullNameField.defaultProps = { label: 'Name' };

export const UserList = (props) => (
<List {...props}>
<Datagrid>
<FullNameField source="lastName" />
</Datagrid>
</List>
);
```

**Tip**: In such custom fields, the `source` is optional. React-admin uses it to determine which column to use for sorting when the column header is clicked. In case you use the `source` property for additional purposes, the sorting can be overridden by the `sortBy` property on any `Field` component.

### Adding A Label To Custom Field Components

When you use one of the react-admin `Field` components in an `Edit`, `Create` or `Show` view, react-admin includes a label on top of the field value, as in the following example:
Expand All @@ -1373,11 +1450,14 @@ When you use one of the react-admin `Field` components in an `Edit`, `Create` or
For your custom fields, however, the label doesn't appear by default. You need to opt in this feature by setting the `addLabel` prop to `true` in the `defaultProps`.

```diff
const FullNameField = ({ record = {} }) => (
<span>
{record.firstName} {record.lastName}
</span>
);
const FullNameField = (props) => {
const record = useRecordContext(props);
return (
<span>
{record.firstName} {record.lastName}
</span>
);
}

FullNameField.defaultProps = {
label: 'Name',
Expand All @@ -1392,10 +1472,10 @@ If you don't use any of these layouts, the `addLabel` trick won't work. You'll h
```jsx
import { Labeled } from 'react-admin';

const MyShowLayout = ({ record }) => (
const MyShowLayout = () => (
<div>
<Labeled label="Name">
<FullNameField record={record} />
<FullNameField />
</Label>
</div>
);
Expand All @@ -1406,7 +1486,7 @@ You can also leverage the default label resolution mechanism by providing the `r
```jsx
import { Labeled } from 'react-admin';

const MyShowLayout = ({ record }) => (
const MyShowLayout = () => (
<div>
<Labeled resource="users" source="name">
<TextField source="name" />
Expand All @@ -1425,10 +1505,12 @@ For such cases, you can use the custom field approach: use the injected `record`
import * as React from "react";
import { EmailField } from 'react-admin';

const ConditionalEmailField = ({ record, ...rest }) =>
record && record.hasEmail
? <EmailField source="email" record={record} {...rest} />
const ConditionalEmailField = (props) => {
const record = useRecordContext(props);
return record && record.hasEmail
? <EmailField source="email" {...props} />
: null;
}

export default ConditionalEmailField;
```
Expand All @@ -1443,14 +1525,17 @@ One solution is to add the label manually in the custom component:
import * as React from "react";
import { Labeled, EmailField } from 'react-admin';

const ConditionalEmailField = ({ record, ...rest }) =>
record && record.hasEmail
const ConditionalEmailField = (props) => {
const record = useRecordContext(props);

return record && record.hasEmail
? (
<Labeled label="Email">
<EmailField source="email" record={record} {...rest} />
<EmailField source="email" {...props} />
</Labeled>
)
: null;
}

export default ConditionalEmailField;
```
Expand Down Expand Up @@ -1525,75 +1610,16 @@ const UserShow = props => (

And now you can use a regular Field component, and the label displays correctly in the Show view.

## Writing Your Own Field Component

If you don't find what you need in the list above, you can write your own Field component. It must be a regular React component, accepting not only a `source` attribute, but also a `record` attribute. React-admin will inject the `record` based on the API response data at render time. The field component only needs to find the `source` in the `record` and display it.

For instance, here is an equivalent of react-admin's `<TextField>` component:

```jsx
import * as React from "react";
import PropTypes from 'prop-types';

const TextField = ({ source, record = {} }) => <span>{record[source]}</span>;

TextField.propTypes = {
label: PropTypes.string,
record: PropTypes.object,
source: PropTypes.string.isRequired,
};

export default TextField;
```

**Tip**: The `label` attribute isn't used in the `render()` method, but react-admin uses it to display the table header.

**Tip**: If you want to support deep field sources (e.g. source values like `author.name`), use [lodash/get](https://www.npmjs.com/package/lodash.get) to replace the simple object lookup:

```jsx
import get from 'lodash/get';
const TextField = ({ source, record = {} }) => <span>{get(record, source)}</span>;
```

If you are not looking for reusability, you can create even simpler components, with no attributes. Let's say an API returns user records with `firstName` and `lastName` properties, and that you want to display a full name in a user list.

```js
{
id: 123,
firstName: 'John',
lastName: 'Doe'
}
```

The component will be:

```jsx
import * as React from "react";
import { List, Datagrid, TextField } from 'react-admin';

const FullNameField = ({ record = {} }) => <span>{record.firstName} {record.lastName}</span>;
FullNameField.defaultProps = { label: 'Name' };

export const UserList = (props) => (
<List {...props}>
<Datagrid>
<FullNameField source="lastName" />
</Datagrid>
</List>
);
```

**Tip**: In such custom fields, the `source` is optional. React-admin uses it to determine which column to use for sorting when the column header is clicked. In case you use the `source` property for additional purposes, the sorting can be overridden by the `sortBy` property on any `Field` component.

### Linking to other records

Your custom Field component might need to display a link to another record. React Admin provides a `linkToRecord(basePath, id[, linkType])` method for this purpose.

```js
import { linkToRecord } from 'react-admin';
import { linkToRecord, useRecordContext } from 'react-admin';
import { Link } from 'react-router-dom';

const MyCustomField = ({ record: post }) => {
const MyCustomField = () => {
const post = useRecordContext(props);
const linkToUser = linkToRecord('/users', post.user_id, 'show');

return <Link to={linkToUser}>{seller.username}</Link>;
Expand Down
Loading

0 comments on commit dd0e793

Please sign in to comment.