Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inserter: Fix Block visibility manager #65700

Merged
merged 5 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { store as noticesStore } from '@wordpress/notices';
import { store as blockEditorStore } from '../../../store';
import { unlock } from '../../../lock-unlock';
import { INSERTER_PATTERN_TYPES } from '../block-patterns-tab/utils';
import { getParsedPattern } from '../../../store/utils';
import { isFiltered } from '../../../store/utils';

/**
* Retrieves the block patterns inserter state.
Expand All @@ -31,48 +31,34 @@ const usePatternsState = (
selectedCategory,
isQuick
) => {
const { patternCategories, allPatterns, userPatternCategories } = useSelect(
const options = useMemo(
() => ( { [ isFiltered ]: !! isQuick } ),
[ isQuick ]
);
const { patternCategories, patterns, userPatternCategories } = useSelect(
( select ) => {
const {
getAllPatterns,
getSettings,
__experimentalGetAllowedPatterns,
} = unlock( select( blockEditorStore ) );
const { getSettings, __experimentalGetAllowedPatterns } = unlock(
select( blockEditorStore )
);
const {
__experimentalUserPatternCategories,
__experimentalBlockPatternCategories,
} = getSettings();
return {
allPatterns: isQuick
? __experimentalGetAllowedPatterns()
: getAllPatterns(),
patterns: __experimentalGetAllowedPatterns(
rootClientId,
options
),
userPatternCategories: __experimentalUserPatternCategories,
patternCategories: __experimentalBlockPatternCategories,
};
},
[ isQuick ]
[ rootClientId, options ]
);
const { getClosestAllowedInsertionPointForPattern } = unlock(
useSelect( blockEditorStore )
);

const patterns = useMemo(
() =>
isQuick
? allPatterns
: allPatterns
.filter( ( { inserter = true } ) => !! inserter )
.map( ( pattern ) => {
return {
...pattern,
get blocks() {
return getParsedPattern( pattern ).blocks;
},
};
} ),
[ isQuick, allPatterns ]
);

const allCategories = useMemo( () => {
const categories = [ ...patternCategories ];
userPatternCategories?.forEach( ( userCategory ) => {
Expand Down
155 changes: 107 additions & 48 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,59 @@ export function getTemplateLock( state, rootClientId ) {
return getBlockListSettings( state, rootClientId )?.templateLock ?? false;
}

/**
* Determines if the given block type is visible in the inserter.
* Note that this is different than whether a block is allowed to be inserted.
* In some cases, the block is not allowed in a given position but
* it should still be visible in the inserter to be able to add it
* to a different position.
*
* @param {Object} state Editor state.
* @param {string|Object} blockNameOrType The block type object, e.g., the response
* from the block directory; or a string name of
* an installed block type, e.g.' core/paragraph'.
*
* @return {boolean} Whether the given block type is allowed to be inserted.
*/
const isBlockVisibleInTheInserter = ( state, blockNameOrType ) => {
let blockType;
let blockName;
if ( blockNameOrType && 'object' === typeof blockNameOrType ) {
blockType = blockNameOrType;
blockName = blockNameOrType.name;
} else {
blockType = getBlockType( blockNameOrType );
blockName = blockNameOrType;
}
if ( ! blockType ) {
return false;
}

const { allowedBlockTypes } = getSettings( state );

const isBlockAllowedInEditor = checkAllowList(
allowedBlockTypes,
blockName,
true
);
if ( ! isBlockAllowedInEditor ) {
return false;
}

// If parent blocks are not visible, child blocks should be hidden too.
if ( !! blockType.parent?.length ) {
return blockType.parent.some(
( name ) =>
isBlockVisibleInTheInserter( state, name ) ||
// Exception for blocks with post-content parent,
// the root level is often consider as "core/post-content".
// This exception should only apply to the post editor ideally though.
name === 'core/post-content'
);
}
return true;
};

/**
* Determines if the given block type is allowed to be inserted into the block list.
* This function is not exported and not memoized because using a memoized selector
Expand All @@ -1558,27 +1611,17 @@ const canInsertBlockTypeUnmemoized = (
blockName,
rootClientId = null
) => {
if ( ! isBlockVisibleInTheInserter( state, blockName ) ) {
return false;
}

let blockType;
if ( blockName && 'object' === typeof blockName ) {
blockType = blockName;
blockName = blockType.name;
} else {
blockType = getBlockType( blockName );
}
if ( ! blockType ) {
return false;
}

const { allowedBlockTypes } = getSettings( state );

const isBlockAllowedInEditor = checkAllowList(
allowedBlockTypes,
blockName,
true
);
if ( ! isBlockAllowedInEditor ) {
return false;
}

const isLocked = !! getTemplateLock( state, rootClientId );
if ( isLocked ) {
Expand Down Expand Up @@ -1972,6 +2015,7 @@ const buildBlockTypeItem =
description: blockType.description,
category: blockType.category,
keywords: blockType.keywords,
parent: blockType.parent,
variations: inserterVariations,
example: blockType.example,
utility: 1, // Deprecated.
Expand Down Expand Up @@ -2067,16 +2111,18 @@ export const getInserterItems = createRegistrySelector( ( select ) =>
)
);
} else {
blockTypeInserterItems = blockTypeInserterItems.map(
( blockType ) => ( {
blockTypeInserterItems = blockTypeInserterItems
.filter( ( blockType ) =>
isBlockVisibleInTheInserter( state, blockType )
)
.map( ( blockType ) => ( {
...blockType,
isAllowedInCurrentRoot: canIncludeBlockTypeInInserter(
state,
blockType,
rootClientId
),
} )
);
} ) );
}

const items = blockTypeInserterItems.reduce(
Expand Down Expand Up @@ -2348,37 +2394,50 @@ const getAllowedPatternsDependants = ( select ) => ( state, rootClientId ) => [
*/
export const __experimentalGetAllowedPatterns = createRegistrySelector(
( select ) => {
return createSelector( ( state, rootClientId = null ) => {
const { getAllPatterns } = unlock( select( STORE_NAME ) );
const patterns = getAllPatterns();
const { allowedBlockTypes } = getSettings( state );
const parsedPatterns = patterns
.filter( ( { inserter = true } ) => !! inserter )
.map( ( pattern ) => {
return {
...pattern,
get blocks() {
return getParsedPattern( pattern ).blocks;
},
};
} );

const availableParsedPatterns = parsedPatterns.filter(
( pattern ) =>
checkAllowListRecursive(
getGrammar( pattern ),
allowedBlockTypes
)
);
const patternsAllowed = availableParsedPatterns.filter(
( pattern ) =>
getGrammar( pattern ).every( ( { blockName: name } ) =>
canInsertBlockType( state, name, rootClientId )
)
);
return createSelector(
(
state,
rootClientId = null,
options = DEFAULT_INSERTER_OPTIONS
) => {
const { getAllPatterns } = unlock( select( STORE_NAME ) );
const patterns = getAllPatterns();
const { allowedBlockTypes } = getSettings( state );
const parsedPatterns = patterns
.filter( ( { inserter = true } ) => !! inserter )
.map( ( pattern ) => {
return {
...pattern,
get blocks() {
return getParsedPattern( pattern ).blocks;
},
};
} );

const availableParsedPatterns = parsedPatterns.filter(
( pattern ) =>
checkAllowListRecursive(
getGrammar( pattern ),
allowedBlockTypes
)
);
const patternsAllowed = availableParsedPatterns.filter(
( pattern ) =>
getGrammar( pattern ).every( ( { blockName: name } ) =>
options[ isFiltered ] !== false
? canInsertBlockType(
state,
name,
rootClientId
)
: isBlockVisibleInTheInserter( state, name )
)
);

return patternsAllowed;
}, getAllowedPatternsDependants( select ) );
return patternsAllowed;
},
getAllowedPatternsDependants( select )
);
}
);

Expand Down
8 changes: 2 additions & 6 deletions test/e2e/specs/editor/various/allowed-patterns.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ test.describe( 'Allowed Patterns', () => {
);
} );

test( 'should show all patterns even if not allowed', async ( {
test( 'should hide patterns with only hidden blocks', async ( {
admin,
page,
} ) => {
Expand All @@ -77,11 +77,7 @@ test.describe( 'Allowed Patterns', () => {
page
.getByRole( 'listbox', { name: 'Block patterns' } )
.getByRole( 'option' )
).toHaveText( [
'Test: Single heading',
'Test: Single paragraph',
'Test: Paragraph inside group',
] );
).toHaveText( [ 'Test: Single heading' ] );
} );
} );
} );
Loading