-
+
get_property( 'label' ) )
+ esc_attr( $_component->get_property( 'label' ) )
)
)
);
diff --git a/header-footer-grid/templates/components/component-logo.php b/header-footer-grid/templates/components/component-logo.php
index c3ac4c0684..7a371697a5 100644
--- a/header-footer-grid/templates/components/component-logo.php
+++ b/header-footer-grid/templates/components/component-logo.php
@@ -12,7 +12,14 @@
use HFG\Core\Builder\Header as HeaderBuilder;
use HFG\Core\Components\Logo;
-$_id = current_component( HeaderBuilder::BUILDER_NAME )->get_id();
+$_component = current_component( HeaderBuilder::BUILDER_NAME );
+
+// Bail when the current component cannot be resolved on the current builder.
+if ( ! $_component instanceof \HFG\Core\Components\Abstract_Component ) {
+ return;
+}
+
+$_id = $_component->get_id();
$device = current_device( HeaderBuilder::BUILDER_NAME );
$show_name = component_setting( Logo::SHOW_TITLE );
diff --git a/header-footer-grid/templates/components/component-nav.php b/header-footer-grid/templates/components/component-nav.php
index c89bf4daf4..64af59c306 100644
--- a/header-footer-grid/templates/components/component-nav.php
+++ b/header-footer-grid/templates/components/component-nav.php
@@ -12,8 +12,15 @@
use HFG\Core\Components\Nav;
use HFG\Core\Builder\Header as HeaderBuilder;
+$_component = current_component( HeaderBuilder::BUILDER_NAME );
+
+// Bail when the current component cannot be resolved on the current builder.
+if ( ! $_component instanceof \HFG\Core\Components\Abstract_Component ) {
+ return;
+}
+
$device_class = isset( $args ) && ! empty( $args ) ? $args['device'] : '';
-$_id = current_component( HeaderBuilder::BUILDER_NAME )->get_id();
+$_id = $_component->get_id();
$style = component_setting( Nav::STYLE_ID, 'style-plain' );
$additional_menu_class = apply_filters( 'neve_additional_menu_class', ' menu-' . $device_class );
diff --git a/tests/test-neve-hfg-component-guards.php b/tests/test-neve-hfg-component-guards.php
new file mode 100644
index 0000000000..c25270fb8e
--- /dev/null
+++ b/tests/test-neve-hfg-component-guards.php
@@ -0,0 +1,142 @@
+previous_state = array(
+ 'builder' => Abstract_Builder::$current_builder,
+ 'builder_component' => Abstract_Builder::$current_component,
+ 'current_component' => Abstract_Component::$current_component,
+ );
+ }
+
+ /**
+ * Restore the static render state.
+ */
+ public function tear_down() {
+ Abstract_Builder::$current_builder = $this->previous_state['builder'];
+ Abstract_Builder::$current_component = $this->previous_state['builder_component'];
+ Abstract_Component::$current_component = $this->previous_state['current_component'];
+
+ parent::tear_down();
+ }
+
+ /**
+ * Get the header builder instance.
+ *
+ * @return Abstract_Builder
+ */
+ private function header_builder() {
+ $builder = Main::get_instance()->get_builder( 'header' );
+
+ $this->assertInstanceOf( Abstract_Builder::class, $builder );
+
+ return $builder;
+ }
+
+ /**
+ * An unknown component id resolves to null instead of raising a warning.
+ */
+ public function test_get_component_returns_null_for_unknown_id() {
+ $this->assertNull( $this->header_builder()->get_component( 'neve-not-a-component' ) );
+ }
+
+ /**
+ * With no component in render context, get_component resolves to null.
+ */
+ public function test_get_component_returns_null_without_render_context() {
+ Abstract_Builder::$current_component = null;
+ Abstract_Component::$current_component = null;
+
+ $this->assertNull( $this->header_builder()->get_component() );
+ }
+
+ /**
+ * A stale component id left in the render context resolves to null.
+ */
+ public function test_get_component_returns_null_for_stale_context() {
+ Abstract_Builder::$current_component = null;
+ Abstract_Component::$current_component = 'neve-stale-component';
+
+ $this->assertNull( $this->header_builder()->get_component() );
+ }
+
+ /**
+ * A registered component is still returned.
+ */
+ public function test_get_component_returns_registered_component() {
+ $builder = $this->header_builder();
+ $components = $builder->get_components();
+
+ if ( empty( $components ) ) {
+ $this->markTestSkipped( 'No header components registered.' );
+ }
+
+ $id = key( $components );
+
+ Abstract_Builder::$current_component = null;
+ Abstract_Component::$current_component = $id;
+
+ $component = $builder->get_component();
+
+ $this->assertInstanceOf( Abstract_Component::class, $component );
+ $this->assertSame( $id, $component->get_id() );
+ }
+
+ /**
+ * Templates that resolve the component from the render context.
+ *
+ * @return array
+ */
+ public function component_template_provider() {
+ return array(
+ 'wrapper' => array( 'component-wrapper' ),
+ 'footer sidebar' => array( 'components/component-footer-sidebar' ),
+ 'logo' => array( 'components/component-logo' ),
+ 'nav' => array( 'components/component-nav' ),
+ );
+ }
+
+ /**
+ * Component templates render nothing when the component is unresolvable.
+ *
+ * @param string $template Template slug.
+ *
+ * @dataProvider component_template_provider
+ */
+ public function test_component_template_renders_nothing_without_component( $template ) {
+ Abstract_Builder::$current_builder = 'header';
+ Abstract_Builder::$current_component = null;
+ Abstract_Component::$current_component = 'neve-stale-component';
+
+ ob_start();
+ Main::get_instance()->load( $template );
+ $output = ob_get_clean();
+
+ $this->assertSame( '', trim( $output ) );
+ }
+}