-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Widgets: Fix creating and editing non-multi widgets #32978
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
/** | ||
* Plugin Name: Gutenberg Test Marquee Widget | ||
* Plugin URI: https://github.com/WordPress/gutenberg | ||
* Author: Gutenberg Team | ||
* | ||
* @package gutenberg-test-marquee-widget | ||
*/ | ||
|
||
/** | ||
* Add a non-WP_Widget marquee widget. | ||
*/ | ||
function marquee_greeting_init() { | ||
wp_register_sidebar_widget( | ||
'marquee_greeting', | ||
'Marquee Greeting', | ||
function() { | ||
$greeting = get_option( 'marquee_greeting', 'Hello!' ); | ||
printf( '<marquee>%s</marquee>', esc_html( $greeting ) ); | ||
} | ||
); | ||
|
||
wp_register_widget_control( | ||
'marquee_greeting', | ||
'Marquee Greeting', | ||
function() { | ||
if ( isset( $_POST['marquee-greeting'] ) ) { | ||
update_option( | ||
'marquee_greeting', | ||
sanitize_text_field( $_POST['marquee-greeting'] ) | ||
); | ||
} | ||
|
||
$greeting = get_option( 'marquee_greeting' ); | ||
?> | ||
<p> | ||
<label for="marquee-greeting">Greeting:</label> | ||
<input | ||
id="marquee-greeting" | ||
class="widefat" | ||
name="marquee-greeting" | ||
type="text" | ||
value="<?php echo esc_attr( $greeting ); ?>" | ||
placeholder="Hello!" | ||
/> | ||
</p> | ||
<?php | ||
} | ||
); | ||
} | ||
add_action( 'init', 'marquee_greeting_init' ); |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -16,7 +16,7 @@ import { | |||||
* External dependencies | ||||||
*/ | ||||||
// eslint-disable-next-line no-restricted-imports | ||||||
import { find, findAll } from 'puppeteer-testing-library'; | ||||||
import { find, findAll, waitFor } from 'puppeteer-testing-library'; | ||||||
import { groupBy, mapValues } from 'lodash'; | ||||||
|
||||||
describe( 'Widgets screen', () => { | ||||||
|
@@ -394,6 +394,111 @@ describe( 'Widgets screen', () => { | |||||
` ); | ||||||
} ); | ||||||
|
||||||
async function addMarquee() { | ||||||
// There will be 2 matches here. | ||||||
// One is the in-between inserter, | ||||||
// and the other one is the button block appender. | ||||||
const [ inlineInserterButton ] = await findAll( { | ||||||
role: 'combobox', | ||||||
name: 'Add block', | ||||||
} ); | ||||||
await inlineInserterButton.click(); | ||||||
|
||||||
// TODO: Convert to find() API from puppeteer-testing-library. | ||||||
const inserterSearchBox = await page.waitForSelector( | ||||||
'aria/Search for blocks and patterns[role="searchbox"]' | ||||||
); | ||||||
await expect( inserterSearchBox ).toHaveFocus(); | ||||||
|
||||||
await page.keyboard.type( 'Marquee' ); | ||||||
|
||||||
const inlineQuickInserter = await find( { | ||||||
role: 'listbox', | ||||||
name: 'Blocks', | ||||||
} ); | ||||||
const marqueeBlockOption = await find( | ||||||
{ | ||||||
role: 'option', | ||||||
}, | ||||||
{ | ||||||
root: inlineQuickInserter, | ||||||
} | ||||||
); | ||||||
await marqueeBlockOption.click(); | ||||||
} | ||||||
|
||||||
it( 'Should add and save the marquee widget', async () => { | ||||||
await activatePlugin( 'gutenberg-test-marquee-widget' ); | ||||||
await visitAdminPage( 'widgets.php' ); | ||||||
|
||||||
await addMarquee(); | ||||||
|
||||||
await find( { | ||||||
selector: '[data-block][data-type="core/legacy-widget"]', | ||||||
} ); | ||||||
Comment on lines
+436
to
+438
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this? |
||||||
|
||||||
const greetingsInput = await find( { | ||||||
selector: '#marquee-greeting', | ||||||
} ); | ||||||
await greetingsInput.click(); | ||||||
await page.keyboard.type( 'Howdy' ); | ||||||
|
||||||
await saveWidgets(); | ||||||
|
||||||
let editedSerializedWidgetAreas = await getSerializedWidgetAreas(); | ||||||
await expect( editedSerializedWidgetAreas ).toMatchInlineSnapshot( ` | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
|
||||||
Object { | ||||||
"sidebar-1": "<marquee>Hello!</marquee>", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it intentional that we type |
||||||
} | ||||||
` ); | ||||||
|
||||||
await page.reload(); | ||||||
|
||||||
editedSerializedWidgetAreas = await getSerializedWidgetAreas(); | ||||||
await expect( editedSerializedWidgetAreas ).toMatchInlineSnapshot( ` | ||||||
Object { | ||||||
"sidebar-1": "<marquee>Hello!</marquee>", | ||||||
} | ||||||
` ); | ||||||
|
||||||
// Add another marquee, it shouldn't be saved | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Nit: this bit doesn't perform any assertions on whether it's saved. As a result, this comment confused me slightly. |
||||||
await addMarquee(); | ||||||
|
||||||
// It takes a moment to load the form, let's wait for it. | ||||||
await waitFor( async () => { | ||||||
const marquees = await findAll( { | ||||||
selector: '[id=marquee-greeting]', | ||||||
} ); | ||||||
if ( marquees.length === 1 ) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are we throwing for here? It would be great if we could convert There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
throw new Error(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want to provide an error message to help future debugging efforts? |
||||||
} | ||||||
} ); | ||||||
Comment on lines
+467
to
+475
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we can use class names to select the inputs rather than using duplicate ids, since that we control the test plugin.
|
||||||
|
||||||
const marquees = await findAll( { | ||||||
selector: '[id=marquee-greeting]', | ||||||
} ); | ||||||
|
||||||
expect( marquees ).toHaveLength( 2 ); | ||||||
await marquees[ 1 ].click(); | ||||||
await page.keyboard.type( 'Second howdy' ); | ||||||
|
||||||
await saveWidgets(); | ||||||
editedSerializedWidgetAreas = await getSerializedWidgetAreas(); | ||||||
await expect( editedSerializedWidgetAreas ).toMatchInlineSnapshot( ` | ||||||
Object { | ||||||
"sidebar-1": "<marquee>Hello!</marquee>", | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we need more comments here as it's really counter-intuitive 😅 . There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We just need a note to say that we're asserting that the second instance of a function based widget is not persisted. We should also note why that's important with references to documentation or technical details. |
||||||
} | ||||||
` ); | ||||||
|
||||||
await page.reload(); | ||||||
const marqueesAfter = await findAll( { | ||||||
selector: '[id=marquee-greeting]', | ||||||
} ); | ||||||
expect( marqueesAfter ).toHaveLength( 1 ); | ||||||
Comment on lines
+493
to
+497
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This bit is asserting on the rendered output. So double checking that the 2nd instance wasn't saved or rendered back to the editor on page reload. |
||||||
|
||||||
await deactivatePlugin( 'gutenberg-test-marquee-widget' ); | ||||||
} ); | ||||||
|
||||||
// Disable reason: We temporary skip this test until we can figure out why it fails sometimes. | ||||||
// eslint-disable-next-line jest/no-disabled-tests | ||||||
it.skip( 'Should duplicate the widgets', async () => { | ||||||
|
@@ -423,6 +528,7 @@ describe( 'Widgets screen', () => { | |||||
"sidebar-1": "<div class=\\"widget widget_block widget_text\\"><div class=\\"widget-content\\"> | ||||||
<p>First Paragraph</p> | ||||||
</div></div>", | ||||||
"wp_inactive_widgets": "", | ||||||
} | ||||||
` ); | ||||||
const initialWidgets = await getWidgetAreaWidgets(); | ||||||
|
@@ -493,6 +599,7 @@ describe( 'Widgets screen', () => { | |||||
<div class=\\"widget widget_block widget_text\\"><div class=\\"widget-content\\"> | ||||||
<p>First Paragraph</p> | ||||||
</div></div>", | ||||||
"wp_inactive_widgets": "", | ||||||
} | ||||||
` ); | ||||||
const editedWidgets = await getWidgetAreaWidgets(); | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,7 +52,10 @@ export default class Control { | |
// a fake but unique number. | ||
this.number = ++lastNumber; | ||
|
||
this.handleFormChange = debounce( this.saveForm.bind( this ), 200 ); | ||
this.handleFormChange = debounce( | ||
this.handleFormChange.bind( this ), | ||
200 | ||
); | ||
this.handleFormSubmit = this.handleFormSubmit.bind( this ); | ||
|
||
this.initDOM(); | ||
|
@@ -214,6 +217,18 @@ export default class Control { | |
} | ||
} | ||
|
||
/** | ||
* Perform a save when a multi widget's form is changed. Non-multi widgets | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is "multi" and "non-multi" a recognised technical term? If not then we should probably add additional context here. |
||
* are saved manually. | ||
* | ||
* @access private | ||
*/ | ||
handleFormChange() { | ||
if ( this.idBase ) { | ||
this.saveForm(); | ||
} | ||
} | ||
|
||
/** | ||
* Perform a save when the control's form is manually submitted. | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -131,7 +131,8 @@ function NotEmpty( { | |
); | ||
} | ||
|
||
const mode = isNavigationMode || ! isSelected ? 'preview' : 'edit'; | ||
const mode = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels like it needs a comment with context to explain why |
||
idBase && ( isNavigationMode || ! isSelected ) ? 'preview' : 'edit'; | ||
|
||
return ( | ||
<> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this test about the implementation of the marquee widget specifically? I'm not sure it is.
Therefore, I wonder whether we ought to rename this test to be more descriptive about the feature actually under test here?