Skip to content

Commit

Permalink
Global style revisions: show change summary on selected item (#56577)
Browse files Browse the repository at this point in the history
* Moving date format setting call into the comoponent
rejigging getLabel function
adding css var

* Testing a message to indicate that the revisions state is the same as the editor state.

* Working on change list, adding translations. WIP

* Adding more translations.

* Revert button-in-item for another day
Reduce depth of changeset and remove unused translations
Display aria-label on button instead of tooltip

* Removing shuffle function and fixing up block spacing translation

* Using the revision has the Map key. This allows us to cache the revision sets themselves and account for changes to Unsaved revisions.

* Used WeakMap in favour of Map for garbage collection, if it helps at all

* Remove hasMore var - unneeded because it's only used once

* Tidying up - remove .map loop

* getGlobalStylesChanges was doing nothing! Removed.

* Using revision + previousRevision combo for cache key to ensure that the results are cached for the same two objects
Returning from cache where maxResults value is smaller than cached results
Added first tests

* Moving maxResults decisions to consuming component. getRevisionChanges returns an unadulterated array.

* Move get blockNames to main component

* Have to use map because WeakMap wants the same reference as the object key.

* Remove the trailing comma on truncated results

* Test commit: listing changes, showing `and n more`

* Test commit: grouping changes using tuples

* Reverting back to comma-separate list of changes
Added e2e assertion

* Swapping order of author name and changes block
Moving everything into the button so it's clickable.

* Don't live in the past, man
  • Loading branch information
ramonjd authored Dec 12, 2023
1 parent 4d61a94 commit 9a46ad1
Show file tree
Hide file tree
Showing 6 changed files with 467 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';

const globalStylesChangesCache = new Map();
const EMPTY_ARRAY = [];

const translationMap = {
caption: __( 'Caption' ),
link: __( 'Link' ),
button: __( 'Button' ),
heading: __( 'Heading' ),
'settings.color': __( 'Color settings' ),
'settings.typography': __( 'Typography settings' ),
'styles.color': __( 'Colors' ),
'styles.spacing': __( 'Spacing' ),
'styles.typography': __( 'Typography' ),
};

const isObject = ( obj ) => obj !== null && typeof obj === 'object';

/**
* Get the translation for a given global styles key.
* @param {string} key A key representing a path to a global style property or setting.
* @param {Record<string,string>} blockNames A key/value pair object of block names and their rendered titles.
* @return {string|undefined} A translated key or undefined if no translation exists.
*/
function getTranslation( key, blockNames ) {
if ( translationMap[ key ] ) {
return translationMap[ key ];
}

const keyArray = key.split( '.' );

if ( keyArray?.[ 0 ] === 'blocks' ) {
const blockName = blockNames[ keyArray[ 1 ] ];
return blockName
? sprintf(
// translators: %s: block name.
__( '%s block' ),
blockName
)
: keyArray[ 1 ];
}

if ( keyArray?.[ 0 ] === 'elements' ) {
return sprintf(
// translators: %s: element name, e.g., heading button, link, caption.
__( '%s element' ),
translationMap[ keyArray[ 1 ] ]
);
}

return undefined;
}

/**
* A deep comparison of two objects, optimized for comparing global styles.
* @param {Object} changedObject The changed object to compare.
* @param {Object} originalObject The original object to compare against.
* @param {string} parentPath A key/value pair object of block names and their rendered titles.
* @return {string[]} An array of paths whose values have changed.
*/
function deepCompare( changedObject, originalObject, parentPath = '' ) {
// We have two non-object values to compare.
if ( ! isObject( changedObject ) && ! isObject( originalObject ) ) {
/*
* Only return a path if the value has changed.
* And then only the path name up to 2 levels deep.
*/
return changedObject !== originalObject
? parentPath.split( '.' ).slice( 0, 2 ).join( '.' )
: undefined;
}

// Enable comparison when an object doesn't have a corresponding property to compare.
changedObject = isObject( changedObject ) ? changedObject : {};
originalObject = isObject( originalObject ) ? originalObject : {};

const allKeys = new Set( [
...Object.keys( changedObject ),
...Object.keys( originalObject ),
] );

let diffs = [];
for ( const key of allKeys ) {
const path = parentPath ? parentPath + '.' + key : key;
const changedPath = deepCompare(
changedObject[ key ],
originalObject[ key ],
path
);
if ( changedPath ) {
diffs = diffs.concat( changedPath );
}
}
return diffs;
}

/**
* Get an array of translated summarized global styles changes.
* Results are cached using a Map() key of `JSON.stringify( { revision, previousRevision } )`.
*
* @param {Object} revision The changed object to compare.
* @param {Object} previousRevision The original object to compare against.
* @param {Record<string,string>} blockNames A key/value pair object of block names and their rendered titles.
* @return {string[]} An array of translated changes.
*/
export default function getRevisionChanges(
revision,
previousRevision,
blockNames
) {
const cacheKey = JSON.stringify( { revision, previousRevision } );

if ( globalStylesChangesCache.has( cacheKey ) ) {
return globalStylesChangesCache.get( cacheKey );
}

/*
* Compare the two revisions with normalized keys.
* The order of these keys determines the order in which
* they'll appear in the results.
*/
const changedValueTree = deepCompare(
{
styles: {
color: revision?.styles?.color,
typography: revision?.styles?.typography,
spacing: revision?.styles?.spacing,
},
blocks: revision?.styles?.blocks,
elements: revision?.styles?.elements,
settings: revision?.settings,
},
{
styles: {
color: previousRevision?.styles?.color,
typography: previousRevision?.styles?.typography,
spacing: previousRevision?.styles?.spacing,
},
blocks: previousRevision?.styles?.blocks,
elements: previousRevision?.styles?.elements,
settings: previousRevision?.settings,
}
);

if ( ! changedValueTree.length ) {
globalStylesChangesCache.set( cacheKey, EMPTY_ARRAY );
return EMPTY_ARRAY;
}

// Remove duplicate results.
const result = [ ...new Set( changedValueTree ) ]
/*
* Translate the keys.
* Remove duplicate or empty translations.
*/
.reduce( ( acc, curr ) => {
const translation = getTranslation( curr, blockNames );
if ( translation && ! acc.includes( translation ) ) {
acc.push( translation );
}
return acc;
}, [] );

globalStylesChangesCache.set( cacheKey, result );

return result;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
__experimentalUseNavigator as useNavigator,
__experimentalConfirmDialog as ConfirmDialog,
Spinner,
__experimentalSpacer as Spacer,
} from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
Expand Down Expand Up @@ -135,7 +134,8 @@ function ScreenRevisions() {
}
}, [ shouldSelectFirstItem, firstRevision ] );

// Only display load button if there is a revision to load and it is different from the current editor styles.
// Only display load button if there is a revision to load,
// and it is different from the current editor styles.
const isLoadButtonEnabled =
!! currentlySelectedRevisionId && ! selectedRevisionMatchesEditorStyles;
const shouldShowRevisions = ! isLoading && revisions.length;
Expand All @@ -156,7 +156,7 @@ function ScreenRevisions() {
{ isLoading && (
<Spinner className="edit-site-global-styles-screen-revisions__loading" />
) }
{ shouldShowRevisions ? (
{ shouldShowRevisions && (
<>
<Revisions
blocks={ blocks }
Expand All @@ -168,6 +168,7 @@ function ScreenRevisions() {
onChange={ selectRevision }
selectedRevisionId={ currentlySelectedRevisionId }
userRevisions={ revisions }
canApplyRevision={ isLoadButtonEnabled }
/>
{ isLoadButtonEnabled && (
<SidebarFixedBottom>
Expand Down Expand Up @@ -215,14 +216,6 @@ function ScreenRevisions() {
</ConfirmDialog>
) }
</>
) : (
<Spacer marginX={ 4 } data-testid="global-styles-no-revisions">
{
// Adding an existing translation here in case these changes are shipped to WordPress 6.3.
// Later we could update to something better, e.g., "There are currently no style revisions.".
__( 'No results found.' )
}
</Spacer>
) }
</>
);
Expand Down
Loading

1 comment on commit 9a46ad1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in 9a46ad1.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7187919081
📝 Reported issues:

Please sign in to comment.