diff --git a/packages/block-editor/src/components/block-visibility/index.js b/packages/block-editor/src/components/block-visibility/index.js
index 69b89e2c9497e6..fc79da9ce01592 100644
--- a/packages/block-editor/src/components/block-visibility/index.js
+++ b/packages/block-editor/src/components/block-visibility/index.js
@@ -3,3 +3,4 @@ export { default as useBlockVisibility } from './use-block-visibility';
export { default as ViewportVisibilityToolbar } from './viewport-toolbar';
export { default as BlockVisibilityViewportMenuItem } from './viewport-menu-item';
export { default as ViewportVisibilityInfo } from './viewport-visibility-info';
+export { getBlockVisibilityLabel } from './utils';
diff --git a/packages/block-editor/src/components/block-visibility/utils.js b/packages/block-editor/src/components/block-visibility/utils.js
index 460283ea291e0b..c026a22f47893e 100644
--- a/packages/block-editor/src/components/block-visibility/utils.js
+++ b/packages/block-editor/src/components/block-visibility/utils.js
@@ -1,3 +1,8 @@
+/**
+ * WordPress dependencies
+ */
+import { __, sprintf } from '@wordpress/i18n';
+
/**
* Internal dependencies
*/
@@ -101,3 +106,38 @@ export function getHideEverywhereCheckboxState( blocks ) {
return null; // Indeterminate: some but not all
}
+
+/**
+ * Get a human-readable label describing which viewports a block is hidden on.
+ *
+ * @param {boolean|Object} blockVisibility The block's visibility metadata.
+ * @return {string|null} A descriptive label, or null if the block is not hidden.
+ */
+export function getBlockVisibilityLabel( blockVisibility ) {
+ // Not hidden at all
+ if ( ! blockVisibility && blockVisibility !== false ) {
+ return null;
+ }
+
+ if ( blockVisibility === false ) {
+ // Hidden on all viewports
+ return __( 'Block is hidden' );
+ }
+
+ if ( blockVisibility?.viewport ) {
+ // Hidden on specific viewports - list them
+ const hiddenViewports = BLOCK_VISIBILITY_VIEWPORT_ENTRIES.filter(
+ ( [ key ] ) => blockVisibility.viewport?.[ key ] === false
+ ).map( ( [ , viewport ] ) => viewport.label );
+
+ if ( hiddenViewports.length > 0 ) {
+ return sprintf(
+ /* translators: %s: comma-separated list of viewport names (Desktop, Tablet, Mobile) */
+ __( 'Block is hidden on %s' ),
+ hiddenViewports.join( ', ' )
+ );
+ }
+ }
+
+ return null;
+}
diff --git a/packages/block-editor/src/components/list-view/block-select-button.js b/packages/block-editor/src/components/list-view/block-select-button.js
index baa349f879ea75..32df3b7458e4ee 100644
--- a/packages/block-editor/src/components/list-view/block-select-button.js
+++ b/packages/block-editor/src/components/list-view/block-select-button.js
@@ -9,6 +9,7 @@ import clsx from 'clsx';
import {
__experimentalHStack as HStack,
__experimentalTruncate as Truncate,
+ Tooltip,
privateApis as componentsPrivateApis,
} from '@wordpress/components';
import { forwardRef } from '@wordpress/element';
@@ -33,6 +34,7 @@ import { useBlockLock } from '../block-lock';
import useListViewImages from './use-list-view-images';
import { store as blockEditorStore } from '../../store';
import { unlock } from '../../lock-unlock';
+import { getBlockVisibilityLabel } from '../block-visibility';
const { Badge } = unlock( componentsPrivateApis );
@@ -60,16 +62,13 @@ function ListViewBlockSelectButton(
context: 'list-view',
} );
const { isLocked } = useBlockLock( clientId );
- const { isBlockHidden, hasPatternName } = useSelect(
+ const { hasPatternName, blockVisibility } = useSelect(
( select ) => {
- const {
- isBlockHiddenAnywhere: _isBlockHidden,
- getBlockAttributes,
- } = unlock( select( blockEditorStore ) );
+ const { getBlockAttributes } = unlock( select( blockEditorStore ) );
+ const attributes = getBlockAttributes( clientId );
return {
- isBlockHidden: _isBlockHidden( clientId ),
- hasPatternName:
- !! getBlockAttributes( clientId )?.metadata?.patternName,
+ hasPatternName: !! attributes?.metadata?.patternName,
+ blockVisibility: attributes?.metadata?.blockVisibility,
};
},
[ clientId ]
@@ -79,6 +78,9 @@ function ListViewBlockSelectButton(
const isSticky = blockInformation?.positionType === 'sticky';
const images = useListViewImages( { clientId, isExpanded } );
+ // Determine visibility label from blockVisibility metadata
+ const visibilityLabel = getBlockVisibilityLabel( blockVisibility );
+
// The `href` attribute triggers the browser's native HTML drag operations.
// When the link is dragged, the element's outerHTML is set in DataTransfer object as text/html.
// We need to clear any HTML drag data to prevent `pasteHandler` from firing
@@ -161,10 +163,15 @@ function ListViewBlockSelectButton(
) ) }
) : null }
- { isBlockHidden && (
-
-
-
+ { !! visibilityLabel && (
+
+
+
+
+
) }
{ shouldShowLockIcon && (
diff --git a/packages/block-editor/src/components/list-view/block.js b/packages/block-editor/src/components/list-view/block.js
index e05036e6e07aa7..0df5b3a57aac5a 100644
--- a/packages/block-editor/src/components/list-view/block.js
+++ b/packages/block-editor/src/components/list-view/block.js
@@ -25,7 +25,7 @@ import {
memo,
} from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
-import { __, sprintf } from '@wordpress/i18n';
+import { __ } from '@wordpress/i18n';
import { BACKSPACE, DELETE } from '@wordpress/keycodes';
import { isShallowEqual } from '@wordpress/is-shallow-equal';
import { __unstableUseShortcutEventMatch as useShortcutEventMatch } from '@wordpress/keyboard-shortcuts';
@@ -54,9 +54,7 @@ import { useBlockRename, BlockRenameModal } from '../block-rename';
import AriaReferencedText from './aria-referenced-text';
import { unlock } from '../../lock-unlock';
import usePasteStyles from '../use-paste-styles';
-import { useBlockVisibility } from '../block-visibility';
-import { deviceTypeKey } from '../../store/private-keys';
-import { BLOCK_VISIBILITY_VIEWPORTS } from '../block-visibility/constants';
+import { getBlockVisibilityLabel } from '../block-visibility';
function ListViewBlock( {
block: { clientId },
@@ -127,51 +125,22 @@ function ListViewBlock( {
const pasteStyles = usePasteStyles();
- const { block, blockName, allowRightClickOverrides, selectedDeviceType } =
- useSelect(
- ( select ) => {
- const { getBlock, getBlockName, getSettings } = unlock(
- select( blockEditorStore )
- );
+ const { block, blockName, allowRightClickOverrides } = useSelect(
+ ( select ) => {
+ const { getBlock, getBlockName, getSettings } = unlock(
+ select( blockEditorStore )
+ );
- return {
- block: getBlock( clientId ),
- blockName: getBlockName( clientId ),
- allowRightClickOverrides:
- getSettings().allowRightClickOverrides,
- selectedDeviceType:
- getSettings()?.[ deviceTypeKey ]?.toLowerCase() ||
- BLOCK_VISIBILITY_VIEWPORTS.desktop.value,
- };
- },
- [ clientId ]
- );
+ return {
+ block: getBlock( clientId ),
+ blockName: getBlockName( clientId ),
+ allowRightClickOverrides:
+ getSettings().allowRightClickOverrides,
+ };
+ },
+ [ clientId ]
+ );
const { canRename } = useBlockRename( blockName );
- // Use hook to get current viewport and if block is currently hidden (accurate viewport detection)
- const { isBlockCurrentlyHidden, currentViewport } = useBlockVisibility( {
- blockVisibility: block?.attributes?.metadata?.blockVisibility,
- deviceType: selectedDeviceType,
- } );
-
- // Determine label based on whether block or parent is hidden
- const blockVisibilityDescription = useMemo( () => {
- if ( isBlockCurrentlyHidden ) {
- if ( block?.attributes?.metadata?.blockVisibility === false ) {
- return __( 'Block is hidden' );
- }
- return sprintf(
- /* translators: %s: viewport name (Desktop, Tablet, Mobile) */
- __( 'Block is hidden on %s' ),
- BLOCK_VISIBILITY_VIEWPORTS[ currentViewport ]?.label ||
- currentViewport
- );
- }
- return null;
- }, [
- isBlockCurrentlyHidden,
- block?.attributes?.metadata?.blockVisibility,
- currentViewport,
- ] );
const showBlockActions =
// When a block hides its toolbar it also hides the block settings menu,
@@ -559,6 +528,11 @@ function ListViewBlock( {
isLocked
);
+ // Determine label based on where block is hidden (not when/current viewport)
+ const blockVisibilityDescription = getBlockVisibilityLabel(
+ block?.attributes?.metadata?.blockVisibility
+ );
+
const hasSiblings = siblingBlockCount > 0;
const hasRenderedMovers = showBlockMovers && hasSiblings;
const moverCellClassName = clsx(