diff --git a/src/DataView/DataViewConfig.php b/src/DataView/DataViewConfig.php
index a4102d4..4aa4cd0 100644
--- a/src/DataView/DataViewConfig.php
+++ b/src/DataView/DataViewConfig.php
@@ -15,6 +15,18 @@ class DataViewConfig {
public readonly string $capability;
public readonly array $storage_options;
+ /**
+ * Whether DataView renders its own success/error notices.
+ *
+ * Set to false when the host environment already renders save notices for
+ * this page — e.g. a settings page under the WordPress "Settings" menu,
+ * where core's options-head.php shows a native "Settings saved." notice for
+ * the `updated=1` redirect. Leaving this true there would double the notice.
+ *
+ * @var bool
+ */
+ public readonly bool $notices;
+
/**
* Full field configurations including repeater sub-fields.
*
@@ -70,6 +82,7 @@ public function __construct( array $config ) {
$this->mode = $config['mode'] ?? 'plural';
$this->capability = $config['capability'] ?? 'manage_options';
$this->storage_options = $config['storage_options'] ?? [];
+ $this->notices = $config['notices'] ?? true;
// Parse field configurations (supports both simple and complex definitions).
$this->field_configs = $this->parse_field_configs( $config['fields'] );
diff --git a/src/DataView/RequestRouter.php b/src/DataView/RequestRouter.php
index c84f446..125acbb 100644
--- a/src/DataView/RequestRouter.php
+++ b/src/DataView/RequestRouter.php
@@ -600,6 +600,10 @@ protected function render_back_link(): void {
* Render success/error notices from query params.
*/
protected function render_notices(): void {
+ if ( ! $this->config->notices ) {
+ return;
+ }
+
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['created'] ) ) {
echo '
' . esc_html( $this->get_label( 'item_created' ) ) . '
';
diff --git a/tests/phpunit/data-view.php b/tests/phpunit/data-view.php
index a56e46a..134ed7a 100644
--- a/tests/phpunit/data-view.php
+++ b/tests/phpunit/data-view.php
@@ -380,6 +380,75 @@ private function get_router( DataView $view ): \Tangible\DataView\RequestRouter
return $property->getValue( $view );
}
+ /**
+ * Render the protected notices output for a DataView's router.
+ */
+ private function render_notices_output( DataView $view ): string {
+ $router = $this->get_router( $view );
+ $method = new \ReflectionMethod( \Tangible\DataView\RequestRouter::class, 'render_notices' );
+ $method->setAccessible( true );
+
+ ob_start();
+ $method->invoke( $router );
+ return ob_get_clean();
+ }
+
+ public function test_notices_config_defaults_to_true(): void {
+ $config = new DataViewConfig( [
+ 'slug' => 'dv_test_notices_default',
+ 'label' => 'Settings',
+ 'fields' => [ 'enabled' => 'boolean' ],
+ ] );
+
+ $this->assertTrue( $config->notices );
+ }
+
+ public function test_notices_config_can_be_disabled(): void {
+ $config = new DataViewConfig( [
+ 'slug' => 'dv_test_notices_off',
+ 'label' => 'Settings',
+ 'fields' => [ 'enabled' => 'boolean' ],
+ 'notices' => false,
+ ] );
+
+ $this->assertFalse( $config->notices );
+ }
+
+ public function test_render_notices_outputs_when_enabled(): void {
+ $_GET['updated'] = '1';
+
+ $view = new DataView( [
+ 'slug' => 'dv_test_notices_on_render',
+ 'label' => 'Settings',
+ 'mode' => 'singular',
+ 'storage' => 'option',
+ 'fields' => [ 'enabled' => 'boolean' ],
+ ] );
+
+ $output = $this->render_notices_output( $view );
+ unset( $_GET['updated'] );
+
+ $this->assertStringContainsString( 'notice-success', $output );
+ }
+
+ public function test_render_notices_suppressed_when_disabled(): void {
+ $_GET['updated'] = '1';
+
+ $view = new DataView( [
+ 'slug' => 'dv_test_notices_off_render',
+ 'label' => 'Settings',
+ 'mode' => 'singular',
+ 'storage' => 'option',
+ 'fields' => [ 'enabled' => 'boolean' ],
+ 'notices' => false,
+ ] );
+
+ $output = $this->render_notices_output( $view );
+ unset( $_GET['updated'] );
+
+ $this->assertSame( '', $output, 'render_notices() must output nothing when notices are disabled' );
+ }
+
public function test_field_type_registry_has_repeater_type(): void {
$registry = new FieldTypeRegistry();