From eb2bcb23818b6dbe8d6625a341e3a05f7f87a4bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 17:00:50 +0000 Subject: [PATCH 01/10] chore(deps): bump axios from 1.7.7 to 1.8.2 Bumps [axios](https://github.com/axios/axios) from 1.7.7 to 1.8.2. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.7.7...v1.8.2) --- updated-dependencies: - dependency-name: axios dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index bca53bc7..ce3d6859 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3433,9 +3433,9 @@ axe-core@^4.10.0: integrity sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w== axios@^1.6.1: - version "1.7.7" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" - integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== + version "1.8.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.2.tgz#fabe06e241dfe83071d4edfbcaa7b1c3a40f7979" + integrity sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" From f892d0a5c178675b856c78ca3c7c794061958403 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 19:57:02 +0000 Subject: [PATCH 02/10] chore(deps): bump symfony/validator from 6.4.4 to 6.4.11 in /api Bumps [symfony/validator](https://github.com/symfony/validator) from 6.4.4 to 6.4.11. - [Release notes](https://github.com/symfony/validator/releases) - [Changelog](https://github.com/symfony/validator/blob/7.2/CHANGELOG.md) - [Commits](https://github.com/symfony/validator/compare/v6.4.4...v6.4.11) --- updated-dependencies: - dependency-name: symfony/validator dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- api/composer.lock | 84 +++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/api/composer.lock b/api/composer.lock index 8eca2315..d3c38c26 100644 --- a/api/composer.lock +++ b/api/composer.lock @@ -2641,16 +2641,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.4.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { @@ -2658,12 +2658,12 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { @@ -2688,7 +2688,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -2704,7 +2704,7 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/doctrine-bridge", @@ -4285,27 +4285,26 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "86fcae159633351e5fd145d1c47de6c528f8caff" + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/86fcae159633351e5fd145d1c47de6c528f8caff", - "reference": "86fcae159633351e5fd145d1c47de6c528f8caff", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", + "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-php80": "^1.14" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4342,7 +4341,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0" }, "funding": [ { @@ -4358,7 +4357,7 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/property-access", @@ -5366,16 +5365,16 @@ }, { "name": "symfony/translation-contracts", - "version": "v3.4.2", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "43810bdb2ddb5400e5c5e778e27b210a0ca83b6b" + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/43810bdb2ddb5400e5c5e778e27b210a0ca83b6b", - "reference": "43810bdb2ddb5400e5c5e778e27b210a0ca83b6b", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", "shasum": "" }, "require": { @@ -5383,12 +5382,12 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { @@ -5424,7 +5423,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.4.2" + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" }, "funding": [ { @@ -5440,7 +5439,7 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/twig-bridge", @@ -5637,16 +5636,16 @@ }, { "name": "symfony/validator", - "version": "v6.4.4", + "version": "v6.4.11", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "1cf92edc9a94d16275efef949fa6748d11cc8f47" + "reference": "4ff41cf10af1de99ad92895411b55c9f309bc2d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/1cf92edc9a94d16275efef949fa6748d11cc8f47", - "reference": "1cf92edc9a94d16275efef949fa6748d11cc8f47", + "url": "https://api.github.com/repos/symfony/validator/zipball/4ff41cf10af1de99ad92895411b55c9f309bc2d8", + "reference": "4ff41cf10af1de99ad92895411b55c9f309bc2d8", "shasum": "" }, "require": { @@ -5693,7 +5692,8 @@ "Symfony\\Component\\Validator\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Tests/", + "/Resources/bin/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -5713,7 +5713,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v6.4.4" + "source": "https://github.com/symfony/validator/tree/v6.4.11" }, "funding": [ { @@ -5729,7 +5729,7 @@ "type": "tidelift" } ], - "time": "2024-02-22T20:27:10+00:00" + "time": "2024-08-30T15:57:55+00:00" }, { "name": "symfony/var-dumper", @@ -6411,7 +6411,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -6419,6 +6419,6 @@ "ext-ctype": "*", "ext-iconv": "*" }, - "platform-dev": {}, - "plugin-api-version": "2.6.0" + "platform-dev": [], + "plugin-api-version": "2.3.0" } From 0b3ad263387ceab39ce7aac8e02b494c5f70a147 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 19:57:02 +0000 Subject: [PATCH 03/10] chore(deps): bump twig/twig from 3.8.0 to 3.11.2 in /api Bumps [twig/twig](https://github.com/twigphp/Twig) from 3.8.0 to 3.11.2. - [Changelog](https://github.com/twigphp/Twig/blob/3.x/CHANGELOG) - [Commits](https://github.com/twigphp/Twig/compare/v3.8.0...v3.11.2) --- updated-dependencies: - dependency-name: twig/twig dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- api/composer.lock | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/api/composer.lock b/api/composer.lock index d3c38c26..036b8d98 100644 --- a/api/composer.lock +++ b/api/composer.lock @@ -6124,30 +6124,38 @@ }, { "name": "twig/twig", - "version": "v3.8.0", + "version": "v3.11.2", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" + "reference": "5b580ec1882b54c98cbd8c0f8a3ca5d1904db6b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/5b580ec1882b54c98cbd8c0f8a3ca5d1904db6b1", + "reference": "5b580ec1882b54c98cbd8c0f8a3ca5d1904db6b1", "shasum": "" }, "require": { "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" + "symfony/polyfill-php80": "^1.22", + "symfony/polyfill-php81": "^1.29" }, "require-dev": { "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -6180,7 +6188,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.8.0" + "source": "https://github.com/twigphp/Twig/tree/v3.11.2" }, "funding": [ { @@ -6192,7 +6200,7 @@ "type": "tidelift" } ], - "time": "2023-11-21T18:54:41+00:00" + "time": "2024-11-06T18:50:16+00:00" }, { "name": "willdurand/negotiation", From 7904b32d9504815b2b8d49385bcd1e7eb39576f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 19:57:39 +0000 Subject: [PATCH 04/10] chore(deps): bump undici from 5.28.4 to 5.28.5 Bumps [undici](https://github.com/nodejs/undici) from 5.28.4 to 5.28.5. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v5.28.4...v5.28.5) --- updated-dependencies: - dependency-name: undici dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index ce3d6859..aef3fb37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9882,9 +9882,9 @@ undici-types@~6.19.2, undici-types@~6.19.8: integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== undici@^5.21.2: - version "5.28.4" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" - integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + version "5.28.5" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.5.tgz#b2b94b6bf8f1d919bc5a6f31f2c01deb02e54d4b" + integrity sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA== dependencies: "@fastify/busboy" "^2.0.0" From 590ba9c43cb811e9e90e369b78c76681d273b470 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Kaiser Date: Mon, 17 Mar 2025 11:25:59 +0100 Subject: [PATCH 05/10] [Storybook] Improve Storybook examples (#600) * storybook: add Book and Review entities * storybook: improve entities attributes * storybook: add story using Field and Input Guessers * storybook: add Advanced Customization story * storybook: add custom menu icons to the guessers story --- api/migrations/Version20250306152729.php | 37 +++ api/src/Entity/Book.php | 77 ++++++ api/src/Entity/Review.php | 69 +++++ src/stories/Custom.stories.tsx | 53 ---- .../custom/AdvancedCustomization.stories.tsx | 247 ++++++++++++++++++ src/stories/custom/UsingGuessers.stories.tsx | 119 +++++++++ 6 files changed, 549 insertions(+), 53 deletions(-) create mode 100644 api/migrations/Version20250306152729.php create mode 100644 api/src/Entity/Book.php create mode 100644 api/src/Entity/Review.php delete mode 100644 src/stories/Custom.stories.tsx create mode 100644 src/stories/custom/AdvancedCustomization.stories.tsx create mode 100644 src/stories/custom/UsingGuessers.stories.tsx diff --git a/api/migrations/Version20250306152729.php b/api/migrations/Version20250306152729.php new file mode 100644 index 00000000..fd915fce --- /dev/null +++ b/api/migrations/Version20250306152729.php @@ -0,0 +1,37 @@ +addSql('CREATE TABLE book (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, isbn VARCHAR(255) DEFAULT NULL, title VARCHAR(255) NOT NULL, description TEXT NOT NULL, author VARCHAR(255) NOT NULL, publication_date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE TABLE review (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, rating SMALLINT NOT NULL, body TEXT NOT NULL, author VARCHAR(255) NOT NULL, publication_date TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, book_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_794381C616A2B381 ON review (book_id)'); + $this->addSql('ALTER TABLE review ADD CONSTRAINT FK_794381C616A2B381 FOREIGN KEY (book_id) REFERENCES book (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('ALTER TABLE review DROP CONSTRAINT FK_794381C616A2B381'); + $this->addSql('DROP TABLE book'); + $this->addSql('DROP TABLE review'); + } +} diff --git a/api/src/Entity/Book.php b/api/src/Entity/Book.php new file mode 100644 index 00000000..e51d68a8 --- /dev/null +++ b/api/src/Entity/Book.php @@ -0,0 +1,77 @@ + 'ASC', + 'isbn' => 'ASC', + 'title' => 'ASC', + 'author' => 'ASC', + 'publicationDate' => 'DESC' +])] +#[ApiFilter(SearchFilter::class, properties: [ + 'id' => 'exact', + 'title' => 'ipartial', + 'author' => 'ipartial' +])] +#[ApiFilter(DateFilter::class, properties: ['publicationDate'])] +class Book +{ + /** The ID of this book */ + #[ORM\Id] + #[ORM\Column] + #[ORM\GeneratedValue] + private ?int $id = null; + + /** The ISBN of this book (or null if doesn't have one) */ + #[ORM\Column(nullable: true)] + public ?string $isbn = null; + + /** The title of this book */ + #[ORM\Column] + #[Assert\NotBlank] + #[ApiProperty(iris: ['http://schema.org/name'])] + public string $title = ''; + + /** The description of this book */ + #[ORM\Column(type: 'text')] + #[Assert\NotBlank] + public string $description = ''; + + /** The author of this book */ + #[ORM\Column] + #[Assert\NotBlank] + public string $author = ''; + + /** The publication date of this book */ + #[ORM\Column] + #[Assert\NotNull] + public ?\DateTimeImmutable $publicationDate = null; + + /** @var Review[] Available reviews for this book */ + #[ORM\OneToMany(mappedBy: 'book', targetEntity: Review::class, cascade: ['persist', 'remove'])] + public iterable $reviews; + + public function __construct() + { + $this->reviews = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } +} diff --git a/api/src/Entity/Review.php b/api/src/Entity/Review.php new file mode 100644 index 00000000..dc7de3f2 --- /dev/null +++ b/api/src/Entity/Review.php @@ -0,0 +1,69 @@ + 'ASC', + 'rating' => 'ASC', + 'author' => 'ASC', + 'publicationDate' => 'DESC' +])] +#[ApiFilter(SearchFilter::class, properties: [ + 'id' => 'exact', + 'body' => 'ipartial', + 'author' => 'ipartial' +])] +#[ApiFilter(NumericFilter::class, properties: ['rating'])] +#[ApiFilter(DateFilter::class, properties: ['publicationDate'])] +class Review +{ + /** The ID of this review */ + #[ORM\Id] + #[ORM\Column] + #[ORM\GeneratedValue] + private ?int $id = null; + + /** The rating of this review (between 0 and 5) */ + #[ORM\Column(type: 'smallint')] + #[Assert\Range(min: 0, max: 5)] + public int $rating = 0; + + /** The body of this review */ + #[ORM\Column(type: 'text')] + #[Assert\NotBlank] + public string $body = ''; + + /** The author of this review */ + #[ORM\Column] + #[Assert\NotBlank] + public string $author = ''; + + /** The publication date of this review */ + #[ORM\Column] + #[Assert\NotNull] + #[ApiProperty(iris: ['http://schema.org/name'])] + public ?\DateTimeImmutable $publicationDate = null; + + /** The book this review is about */ + #[ORM\ManyToOne(inversedBy: 'reviews')] + #[Assert\NotNull] + public ?Book $book = null; + + public function getId(): ?int + { + return $this->id; + } +} diff --git a/src/stories/Custom.stories.tsx b/src/stories/Custom.stories.tsx deleted file mode 100644 index a68b6281..00000000 --- a/src/stories/Custom.stories.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import { HydraAdmin } from '../hydra'; -import ResourceGuesser from '../core/ResourceGuesser'; -import ListGuesser from '../list/ListGuesser'; -import ShowGuesser from '../show/ShowGuesser'; -import FieldGuesser from '../field/FieldGuesser'; -import EditGuesser from '../edit/EditGuesser'; -import InputGuesser from '../input/InputGuesser'; -import CreateGuesser from '../create/CreateGuesser'; - -export default { - title: 'Admin/Custom', - parameters: { - layout: 'fullscreen', - }, -}; - -const GreetingList = () => ( - - - -); - -const GreetingShow = () => ( - - - -); - -const GreetingEdit = () => ( - - - -); - -const GreetingCreate = () => ( - - - -); - -export const Custom = () => ( - - - -); diff --git a/src/stories/custom/AdvancedCustomization.stories.tsx b/src/stories/custom/AdvancedCustomization.stories.tsx new file mode 100644 index 00000000..c3c90b84 --- /dev/null +++ b/src/stories/custom/AdvancedCustomization.stories.tsx @@ -0,0 +1,247 @@ +import AutoStoriesIcon from '@mui/icons-material/AutoStories'; +import ReviewsIcon from '@mui/icons-material/Reviews'; +import { Rating, Stack } from '@mui/material'; +import React from 'react'; +import type { InputProps } from 'react-admin'; +import { + AutocompleteInput, + Create, + Datagrid, + DateField, + Edit, + Labeled, + Layout, + List, + NumberField, + ReferenceArrayField, + ReferenceField, + ReferenceInput, + Show, + SimpleForm, + SimpleList, + SimpleShowLayout, + TabbedShowLayout, + TextField, + TextInput, + WithRecord, + WrapperField, + defaultDarkTheme, + defaultLightTheme, + required, + useInput, +} from 'react-admin'; +import ResourceGuesser from '../../core/ResourceGuesser'; +import FieldGuesser from '../../field/FieldGuesser'; +import { HydraAdmin } from '../../hydra'; +import InputGuesser from '../../input/InputGuesser'; + +export default { + title: 'Admin/Custom/AdvancedCustomization', + parameters: { + layout: 'fullscreen', + }, +}; + +const BookCreate = () => ( + + + + + + + + + + + +); + +const BookEdit = () => ( + + + + + + + + + + + +); + +const BookShow = () => ( + + + + + + + + + + + + + + + review.author + .split(' ') + .map((name: string) => name[0]) + .join('') + } + // eslint-disable-next-line react/no-unstable-nested-components + tertiaryText={(review) => ( + + )} + /> + + + + +); + +const BookList = () => ( + + + + + + + + + +); + +const RatingInput = (props: InputProps) => { + const { field } = useInput(props); + return ( + { + field.onChange(value); + }} + /> + ); +}; + +const filterToBookQuery = (searchText: string) => ({ + title: `%${searchText}%`, +}); + +const ReviewCreate = () => ( + + + + + + + + + + + + +); + +const ReviewEdit = () => ( + + + + + + + + + + + + + + + +); + +const ReviewShow = () => ( + + + + + + + ( + + )} + /> + + + + +); + +const ReviewList = () => ( + + + + + + ( + + )} + /> + + + + +); + +export const AdvancedCustomization = () => ( + + + + +); diff --git a/src/stories/custom/UsingGuessers.stories.tsx b/src/stories/custom/UsingGuessers.stories.tsx new file mode 100644 index 00000000..79a59bee --- /dev/null +++ b/src/stories/custom/UsingGuessers.stories.tsx @@ -0,0 +1,119 @@ +import AutoStoriesIcon from '@mui/icons-material/AutoStories'; +import ReviewsIcon from '@mui/icons-material/Reviews'; +import React from 'react'; +import { HydraAdmin } from '../../hydra'; +import ResourceGuesser from '../../core/ResourceGuesser'; +import ListGuesser from '../../list/ListGuesser'; +import ShowGuesser from '../../show/ShowGuesser'; +import FieldGuesser from '../../field/FieldGuesser'; +import EditGuesser from '../../edit/EditGuesser'; +import InputGuesser from '../../input/InputGuesser'; +import CreateGuesser from '../../create/CreateGuesser'; + +export default { + title: 'Admin/Custom/UsingGuessers', + parameters: { + layout: 'fullscreen', + }, +}; + +const BookCreate = () => ( + + + + + + + +); + +const BookEdit = () => ( + + + + + + + +); + +const BookShow = () => ( + + + + + + + + +); + +const BookList = () => ( + + + + + + + +); + +const ReviewCreate = () => ( + + + + + + + +); + +const ReviewEdit = () => ( + + + + + + + +); + +const ReviewShow = () => ( + + + + + + + +); + +const ReviewList = () => ( + + + + + + +); + +export const UsingGuessers = () => ( + + + + +); From abc22f6032b5e02a131fbc382936fcdefddf865d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Kaiser Date: Tue, 18 Mar 2025 09:39:10 +0100 Subject: [PATCH 06/10] (fix)ResourceGuesser: Fix `` ignores `recordRepresentation` prop (#611) * (fix)ResourceGuesser: Fix ResourceGuesser ignores recordRepresentation prop * fix advanced story * fix tests --- src/core/ResourceGuesser.test.tsx | 83 +++++++++++++++++++ src/core/ResourceGuesser.tsx | 3 + .../custom/AdvancedCustomization.stories.tsx | 2 +- 3 files changed, 87 insertions(+), 1 deletion(-) diff --git a/src/core/ResourceGuesser.test.tsx b/src/core/ResourceGuesser.test.tsx index 8fac3b7c..66f7766d 100644 --- a/src/core/ResourceGuesser.test.tsx +++ b/src/core/ResourceGuesser.test.tsx @@ -1,6 +1,65 @@ import React from 'react'; +import { + AdminContext, + useGetOne, + useGetRecordRepresentation, +} from 'react-admin'; import ReactTestRenderer from 'react-test-renderer/shallow'; +import { Resource } from '@api-platform/api-doc-parser'; +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; import ResourceGuesser from './ResourceGuesser.js'; +import SchemaAnalyzerContext from '../introspection/SchemaAnalyzerContext.js'; +import schemaAnalyzer from '../hydra/schemaAnalyzer.js'; +import type { + ApiPlatformAdminDataProvider, + ApiPlatformAdminRecord, +} from '../types.js'; +import { API_FIELDS_DATA } from '../__fixtures__/parsedData.js'; + +const hydraSchemaAnalyzer = schemaAnalyzer(); +const dataProvider: ApiPlatformAdminDataProvider = { + getList: () => Promise.resolve({ data: [], total: 0 }), + getMany: () => Promise.resolve({ data: [] }), + getManyReference: () => Promise.resolve({ data: [], total: 0 }), + update: () => + Promise.resolve({ data: { id: 'id' } } as { data: RecordType }), + updateMany: () => Promise.resolve({ data: [] }), + create: () => + Promise.resolve({ data: { id: 'id' } } as { data: RecordType }), + delete: () => + Promise.resolve({ data: { id: 'id' } } as { data: RecordType }), + deleteMany: () => Promise.resolve({ data: [] }), + getOne: () => + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + Promise.resolve({ + data: { + id: '/users/123', + fieldA: 'fieldA value', + fieldB: 'fieldB value', + deprecatedField: 'deprecatedField value', + title: 'Title', + body: 'Body', + }, + }), + introspect: () => + Promise.resolve({ + data: { + entrypoint: 'entrypoint', + resources: [ + new Resource('users', '/users', { + fields: API_FIELDS_DATA, + readableFields: API_FIELDS_DATA, + writableFields: API_FIELDS_DATA, + parameters: [], + }), + ], + }, + }), + subscribe: () => Promise.resolve({ data: null }), + unsubscribe: () => Promise.resolve({ data: null }), +}; describe('', () => { const renderer = ReactTestRenderer.createRenderer(); @@ -60,4 +119,28 @@ describe('', () => { expect(renderer.getRenderOutput()).toMatchSnapshot(); }); + + test('supports recordRepresentation', async () => { + const TestComponent = () => { + const { data: user } = useGetOne('users', { id: '/users/123' }); + const getRecordRepresentation = useGetRecordRepresentation('users'); + if (!user) { + return 'loading'; + } + return getRecordRepresentation(user); + }; + render( + + + } + recordRepresentation="fieldA" + /> + + , + ); + + await screen.findByText('fieldA value'); + }); }); diff --git a/src/core/ResourceGuesser.tsx b/src/core/ResourceGuesser.tsx index 6f74ab4d..f47c8151 100644 --- a/src/core/ResourceGuesser.tsx +++ b/src/core/ResourceGuesser.tsx @@ -58,6 +58,7 @@ export const IntrospectedResourceGuesser = ({ name: resource, icon: props.icon, options: props.options, + recordRepresentation: props.recordRepresentation, hasList, hasEdit, hasCreate, @@ -69,6 +70,7 @@ export const IntrospectedResourceGuesser = ({ resource, props.icon, props.options, + props.recordRepresentation, hasList, hasEdit, hasCreate, @@ -104,6 +106,7 @@ ResourceGuesser.registerResource = ( name: props.name, icon: props.icon, options: props.options, + recordRepresentation: props.recordRepresentation, hasList: true, hasEdit: true, hasCreate: true, diff --git a/src/stories/custom/AdvancedCustomization.stories.tsx b/src/stories/custom/AdvancedCustomization.stories.tsx index c3c90b84..7955b16d 100644 --- a/src/stories/custom/AdvancedCustomization.stories.tsx +++ b/src/stories/custom/AdvancedCustomization.stories.tsx @@ -231,7 +231,7 @@ export const AdvancedCustomization = () => ( show={BookShow} edit={BookEdit} create={BookCreate} - recordRepresentation="name" + recordRepresentation="title" icon={AutoStoriesIcon} /> Date: Wed, 9 Apr 2025 09:16:04 +0200 Subject: [PATCH 07/10] fix(hydra): server-side validation errors (#613) --- src/hydra/fetchHydra.test.ts | 26 ++++++++++++++++++++++++++ src/hydra/schemaAnalyzer.ts | 13 +++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/hydra/fetchHydra.test.ts b/src/hydra/fetchHydra.test.ts index a3c2dfd0..d1f9e033 100644 --- a/src/hydra/fetchHydra.test.ts +++ b/src/hydra/fetchHydra.test.ts @@ -55,6 +55,32 @@ test.each([ 'At least one product must be selected if policy is restricted.', }, ], + [ + 'problem+json', + { + '@context': '/contexts/ConstraintViolation', + '@id': '/validation_errors/2881c032-660f-46b6-8153-d352d9706640', + '@type': 'ConstraintViolation', + status: 422, + violations: [ + { + propertyPath: 'isbn', + 'ConstraintViolation/message': + 'This value is neither a valid ISBN-10 nor a valid ISBN-13.', + 'ConstraintViolation/code': '2881c032-660f-46b6-8153-d352d9706640', + }, + ], + detail: + 'isbn: This value is neither a valid ISBN-10 nor a valid ISBN-13.', + description: + 'isbn: This value is neither a valid ISBN-10 nor a valid ISBN-13.', + type: '/validation_errors/2881c032-660f-46b6-8153-d352d9706640', + title: 'An error occurred', + }, + { + isbn: 'This value is neither a valid ISBN-10 nor a valid ISBN-13.', + }, + ], ])( '%s violation list expanding', async (format: string, resBody: object, expected: object) => { diff --git a/src/hydra/schemaAnalyzer.ts b/src/hydra/schemaAnalyzer.ts index 36b8da86..082cb3e3 100644 --- a/src/hydra/schemaAnalyzer.ts +++ b/src/hydra/schemaAnalyzer.ts @@ -68,6 +68,10 @@ const getFieldType = (field: Field) => { } }; +const getViolationMessage = (violation: JsonLdObj, base: string) => + violation[`${base}#message`] ?? + violation[`${base}#ConstraintViolation/message`]; + const getSubmissionErrors = (error: HttpError) => { if (!error.body?.[0]) { return null; @@ -84,15 +88,16 @@ const getSubmissionErrors = (error: HttpError) => { const violations: SubmissionErrors = content[violationKey].reduce( (previousViolations: SubmissionErrors, violation: JsonLdObj) => - !violation[`${base}#propertyPath`] || !violation[`${base}#message`] + !violation[`${base}#propertyPath`] || + !getViolationMessage(violation, base) ? previousViolations : { ...previousViolations, [(violation[`${base}#propertyPath`] as JsonLdObj[])[0]?.[ '@value' - ] as string]: (violation[`${base}#message`] as JsonLdObj[])[0]?.[ - '@value' - ], + ] as string]: ( + getViolationMessage(violation, base) as JsonLdObj[] + )[0]?.['@value'], }, {}, ); From d67bea7e0d0998e260b5617fa688e5743322c111 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Kaiser Date: Fri, 11 Apr 2025 09:22:51 +0200 Subject: [PATCH 08/10] fix(storybook): Add story with OpenApiAdmin (#614) --- api/config/packages/api_platform.yaml | 1 + src/stories/Basic.stories.ts | 26 ---------------- src/stories/Basic.stories.tsx | 44 +++++++++++++++++++++++++++ src/stories/Configure.mdx | 3 -- 4 files changed, 45 insertions(+), 29 deletions(-) delete mode 100644 src/stories/Basic.stories.ts create mode 100644 src/stories/Basic.stories.tsx delete mode 100644 src/stories/Configure.mdx diff --git a/api/config/packages/api_platform.yaml b/api/config/packages/api_platform.yaml index d724b344..4dd2e726 100644 --- a/api/config/packages/api_platform.yaml +++ b/api/config/packages/api_platform.yaml @@ -6,6 +6,7 @@ api_platform: include_type: true formats: jsonld: ['application/ld+json'] + json: ["application/json"] docs_formats: jsonld: ['application/ld+json'] jsonopenapi: ['application/vnd.openapi+json'] diff --git a/src/stories/Basic.stories.ts b/src/stories/Basic.stories.ts deleted file mode 100644 index 8fe6c89c..00000000 --- a/src/stories/Basic.stories.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { within } from '@storybook/test'; -import Basic from './Basic'; - -const meta = { - title: 'Admin/Basic', - component: Basic, - tags: ['autodocs'], - parameters: { - layout: 'fullscreen', - }, -} satisfies Meta; - -export default meta; - -type Story = StoryObj; - -export const Admin: Story = { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - await canvas.findByText('Greetings'); - }, - args: { - entrypoint: process.env.ENTRYPOINT, - }, -}; diff --git a/src/stories/Basic.stories.tsx b/src/stories/Basic.stories.tsx new file mode 100644 index 00000000..a086bd37 --- /dev/null +++ b/src/stories/Basic.stories.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { within } from '@storybook/test'; +import { HydraAdmin, type HydraAdminProps } from '../hydra'; +import { OpenApiAdmin } from '../openapi'; + +/** + * # Basic `` component + * The `` component without any parameter. + */ +const Basic = ({ entrypoint }: BasicProps) => ( + +); + +interface BasicProps extends Pick {} + +const meta = { + title: 'Admin/Basic', + component: Basic, + parameters: { + layout: 'fullscreen', + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Hydra: Story = { + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + await canvas.findByText('Greetings'); + }, + args: { + entrypoint: process.env.ENTRYPOINT, + }, +}; + +export const OpenApi = () => ( + +); diff --git a/src/stories/Configure.mdx b/src/stories/Configure.mdx deleted file mode 100644 index cd58d037..00000000 --- a/src/stories/Configure.mdx +++ /dev/null @@ -1,3 +0,0 @@ -import { Meta } from "@storybook/blocks"; - - \ No newline at end of file From a1b46ca05fbb9fa8b822383a80a3fe8080e51355 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Kaiser Date: Fri, 16 May 2025 11:48:04 +0200 Subject: [PATCH 09/10] [chore] update CHANGELOG.md to redirect to release notes --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5036fe47..b9bdeb86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Newer versions + +See release notes: https://github.com/api-platform/admin/releases + ## 4.0.0 * Compatibility with react-admin v5 From 3ca0d96a1546465b6ad2028baceabb9fece8bf5d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Kaiser Date: Fri, 16 May 2025 11:53:47 +0200 Subject: [PATCH 10/10] v4.0.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index da6a3cc2..f7136f8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@api-platform/admin", - "version": "4.0.6", + "version": "4.0.7", "description": "Automatic administration interface for Hydra-enabled APIs.", "files": [ "*.md", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy