diff --git a/.github/workflows/end2end-test.yml b/.github/workflows/end2end-test.yml index 777549e334aa96..5a9750c6bb0456 100644 --- a/.github/workflows/end2end-test.yml +++ b/.github/workflows/end2end-test.yml @@ -88,6 +88,8 @@ jobs: npm run wp-env start - name: Run the tests + env: + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 run: | xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run test:e2e:playwright -- --shard=${{ matrix.part }}/${{ matrix.totalParts }} diff --git a/.github/workflows/stale-issue-gardening.yml b/.github/workflows/stale-issue-gardening.yml index 0bdb1cfbf0cefd..cbeb04ead53214 100644 --- a/.github/workflows/stale-issue-gardening.yml +++ b/.github/workflows/stale-issue-gardening.yml @@ -27,8 +27,8 @@ jobs: remove-stale-when-updated: true stale-issue-label: '[Status] Stale' - name: 'Flaky test issues without activity' - message: 'This issue has gone 30 days without any activity.' - days-before-stale: 30 + message: 'This issue has gone 15 days without any activity.' + days-before-stale: 15 days-before-close: 1 only-labels: '[Type] Flaky Test' remove-stale-when-updated: true diff --git a/bin/plugin/commands/performance.js b/bin/plugin/commands/performance.js index 4be675a0a5d40d..bdc38347e40c86 100644 --- a/bin/plugin/commands/performance.js +++ b/bin/plugin/commands/performance.js @@ -87,6 +87,7 @@ async function runTestSuite( testSuite, testRunnerDir, runKey ) { testRunnerDir, { ...process.env, + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1', WP_ARTIFACTS_PATH: ARTIFACTS_PATH, RESULTS_ID: runKey, } diff --git a/changelog.txt b/changelog.txt index 1814106e5e5092..92fa2690b15d68 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,328 @@ == Changelog == += 17.2.0-rc.1 = + + + +## Changelog + +### Features + +#### Modules API +- Interactivity API: Use modules instead of scripts in the frontend. ([56143](https://github.com/WordPress/gutenberg/pull/56143)) + + +### Enhancements + +- Add translator comments for strings containing date formats. ([56531](https://github.com/WordPress/gutenberg/pull/56531)) +- Block Settings: Only display parent block selector on small screens. ([56431](https://github.com/WordPress/gutenberg/pull/56431)) +- Block Theme Preview: Display the theme name on the activate button. ([55752](https://github.com/WordPress/gutenberg/pull/55752)) +- Core data revisions: Extend support to other post types. ([56353](https://github.com/WordPress/gutenberg/pull/56353)) +- Improve tooltip for parent blocks on the block toolbar. ([56146](https://github.com/WordPress/gutenberg/pull/56146)) +- Simplify template author token. ([56566](https://github.com/WordPress/gutenberg/pull/56566)) +- Style engine: Allow CSS var output for fontSize and fontFamily and update documentation. ([56528](https://github.com/WordPress/gutenberg/pull/56528)) +- Try: Change "Detach pattern" to "Detach". ([56323](https://github.com/WordPress/gutenberg/pull/56323)) +- useEntityRecord: Improve unit tests. ([56415](https://github.com/WordPress/gutenberg/pull/56415)) + +#### Components +- Add focus rings to focusable disabled buttons. ([56383](https://github.com/WordPress/gutenberg/pull/56383)) +- DropdownMenu V2 tweaks. ([56041](https://github.com/WordPress/gutenberg/pull/56041)) +- DropdownMenu V2: Add support for rendering in legacy popover slot. ([56342](https://github.com/WordPress/gutenberg/pull/56342)) +- FormToggle: Refine animation. ([56515](https://github.com/WordPress/gutenberg/pull/56515)) +- Slot: Add styles prop to bubblesVirtually version. ([56428](https://github.com/WordPress/gutenberg/pull/56428)) +- Tabs: Cleanup and improvements. ([56224](https://github.com/WordPress/gutenberg/pull/56224)) +- Try Ariakit Select for new CustomSelectControl component. ([55790](https://github.com/WordPress/gutenberg/pull/55790)) + +#### Data Views +- Data list view: Make filter row, table header, and pagination sticky. ([56157](https://github.com/WordPress/gutenberg/pull/56157)) +- Simplify dataviews view button. ([56485](https://github.com/WordPress/gutenberg/pull/56485)) +- Update data view menu item actions. ([56398](https://github.com/WordPress/gutenberg/pull/56398)) + +#### Global Styles +- Global style revisions: Redesign style revision items. ([55913](https://github.com/WordPress/gutenberg/pull/55913)) +- Global styles revisions: Migrate API call to getRevisions(). ([56349](https://github.com/WordPress/gutenberg/pull/56349)) +- Style Revisions: Remove style revisions dropdown menu. ([56454](https://github.com/WordPress/gutenberg/pull/56454)) + +#### Site Editor +- Add 'View site' action to 'Site updated' snackbar. ([52693](https://github.com/WordPress/gutenberg/pull/52693)) +- Add the Post Author component to the Page sidebar. ([56368](https://github.com/WordPress/gutenberg/pull/56368)) +- Redirect to main page menu if page record not found. ([56177](https://github.com/WordPress/gutenberg/pull/56177)) + +#### Block Editor +- Drag and drop: Allow dragging to the beginning and end of a document. ([56070](https://github.com/WordPress/gutenberg/pull/56070)) +- List View: Expand state if a block is dragged to within a collapsed block in the editor canvas. ([56493](https://github.com/WordPress/gutenberg/pull/56493)) + +#### Layout +- Add layout classes to legacy Group inner container. ([56130](https://github.com/WordPress/gutenberg/pull/56130)) +- Add setting to disable custom content size controls. ([56236](https://github.com/WordPress/gutenberg/pull/56236)) + +#### Patterns +- Small tweaks to CreatePatternModal. ([56016](https://github.com/WordPress/gutenberg/pull/56016)) +- Update Labels in Block Inserter (block patterns tab). ([55986](https://github.com/WordPress/gutenberg/pull/55986)) + +#### Icons +- Update trash icon. ([56569](https://github.com/WordPress/gutenberg/pull/56569)) + +#### Block Library +- Disable block renaming support for Nav Link block. ([56425](https://github.com/WordPress/gutenberg/pull/56425)) + +#### Distraction Free +- Add top toolbar to distraction free mode. ([56295](https://github.com/WordPress/gutenberg/pull/56295)) + +#### CSS & Styling +- Gallery Block: Use styled scrollbars for image captions. ([56252](https://github.com/WordPress/gutenberg/pull/56252)) + +#### Typography +- Font Library: Remove insecure properties. ([56230](https://github.com/WordPress/gutenberg/pull/56230)) + + +### New APIs + +- Revisions: Add new selectors to fetch entity revisions. ([54046](https://github.com/WordPress/gutenberg/pull/54046)) + +#### Interactivity API +- Migration to the new `store()` API. ([55459](https://github.com/WordPress/gutenberg/pull/55459)) + + +### Bug Fixes + +- Block Editor: Undeprecate the '__experimentalImageSizeControl' component. ([56414](https://github.com/WordPress/gutenberg/pull/56414)) +- Core data: Harmonize getRevision selector and resolver function signatures. ([56416](https://github.com/WordPress/gutenberg/pull/56416)) +- Editor styles: Scope without adding specificity. ([56564](https://github.com/WordPress/gutenberg/pull/56564)) +- Fix Restore Post title placeholder. ([56580](https://github.com/WordPress/gutenberg/pull/56580)) +- Post Schedule Panel: Remove text overflow ellipsis. ([56319](https://github.com/WordPress/gutenberg/pull/56319)) +- PostCSS style transformation: Fail gracefully instead of throwing an error. ([56093](https://github.com/WordPress/gutenberg/pull/56093)) +- Rich text: Pad multiple spaces through en/em replacement. ([56341](https://github.com/WordPress/gutenberg/pull/56341)) +- Site Editor Sidebar: Fix actions vertical alignment. ([56218](https://github.com/WordPress/gutenberg/pull/56218)) +- Site Editor: Add a fallback template showing the title and content for the post only mode. ([56509](https://github.com/WordPress/gutenberg/pull/56509)) +- useEntityRecord: Do not trigger REST API requests when disabled. ([56108](https://github.com/WordPress/gutenberg/pull/56108)) + +#### Block Library +- File block: Remove anchor tag when copy pasting to file name. ([56508](https://github.com/WordPress/gutenberg/pull/56508)) +- Fix label of columns inspector panel. ([56647](https://github.com/WordPress/gutenberg/pull/56647)) +- Post Template: Fix incorrect offset query. ([56440](https://github.com/WordPress/gutenberg/pull/56440)) + +#### Block Editor +- (RichText)(Workaround)(17.1.x) Fallback to a string arg in `collapseWhiteSpace()` if `value` is not a string. ([56570](https://github.com/WordPress/gutenberg/pull/56570)) +- Cover block: Pass dropZoneElement reference to fix dragging within cover block area. ([56312](https://github.com/WordPress/gutenberg/pull/56312)) +- useMovingAnimation: Clear translate3d rule when animation is finished. ([56410](https://github.com/WordPress/gutenberg/pull/56410)) + +#### Components +- Design Tools: Fix last ToolsPanelItem styling. ([56536](https://github.com/WordPress/gutenberg/pull/56536)) +- Fix FormTokenField suggestions broken scrollbar when `__experimentalExpandOnFocus` is defined. ([56426](https://github.com/WordPress/gutenberg/pull/56426)) +- Tabs: Fix flaky unit tests. ([55950](https://github.com/WordPress/gutenberg/pull/55950)) + +#### Global Styles +- Additional CSS: Fix on change validation. ([56434](https://github.com/WordPress/gutenberg/pull/56434)) +- Global styles revisions: Update isResolving flag. ([56491](https://github.com/WordPress/gutenberg/pull/56491)) +- Spacing: Fix block error if spacing unit array empty in theme.json. ([56306](https://github.com/WordPress/gutenberg/pull/56306)) + +#### CSS & Styling +- Reduce specificity of default Cover text color styles. ([56411](https://github.com/WordPress/gutenberg/pull/56411)) +- Restore Post Title visual styles in Code View mode. ([56582](https://github.com/WordPress/gutenberg/pull/56582)) + +#### Saving +- Editor: Reinstate anonymous callback for saved post state. ([56529](https://github.com/WordPress/gutenberg/pull/56529)) + +#### Post Editor +- Save post button: Avoid extra re-renders when enablng/disabling tooltip. ([56502](https://github.com/WordPress/gutenberg/pull/56502)) + +#### Plugin +- Update Readme.txt tested up to 6.4. ([56427](https://github.com/WordPress/gutenberg/pull/56427)) + +#### Site Editor +- Fix template resolution for templates assigned as home page. ([56418](https://github.com/WordPress/gutenberg/pull/56418)) + +#### Patterns +- Fix issue with template in replace template screen. ([56407](https://github.com/WordPress/gutenberg/pull/56407)) + +#### Layout +- Fix issue where layout classnames are injected for blocks without layout support. ([56187](https://github.com/WordPress/gutenberg/pull/56187)) + +#### Typography +- Font Library: Fix fonts not displaying correctly. ([55393](https://github.com/WordPress/gutenberg/pull/55393)) + +#### Colors +- Duotone: Backport from Core to fix filters in classic themes. ([54778](https://github.com/WordPress/gutenberg/pull/54778)) + + +### Accessibility + +- Migrating `StyleBook` to use updated `Composite` implementation. ([55344](https://github.com/WordPress/gutenberg/pull/55344)) + +#### Data Views +- DataViews: Make disabled pagination buttons focusable. ([56422](https://github.com/WordPress/gutenberg/pull/56422)) + +#### Block Library +- Image Block: Enable image block to be selected correctly when clicked. ([56043](https://github.com/WordPress/gutenberg/pull/56043)) + +#### Post Editor +- Tooltip: Don't render buttons tooltip when show button text labels is enabled. ([55842](https://github.com/WordPress/gutenberg/pull/55842)) + +#### Components +- Improve `Button` saving state accessibility. ([55547](https://github.com/WordPress/gutenberg/pull/55547)) + +#### Patterns +- Fix focus loss after converting to a synced pattern. ([55473](https://github.com/WordPress/gutenberg/pull/55473)) + + +### Performance + +- Avoid calling postcss when not needed. ([56601](https://github.com/WordPress/gutenberg/pull/56601)) +- Block Editor: Optimize 'Connections' inspector controls. ([56443](https://github.com/WordPress/gutenberg/pull/56443)) + +#### Global Styles +- Make search more responsive for block type list. ([56139](https://github.com/WordPress/gutenberg/pull/56139)) + + +### Experiments + +#### Data Views +- DataViews: Document `view.layout`. ([56637](https://github.com/WordPress/gutenberg/pull/56637)) +- DataViews: Extract common constants to file. ([56251](https://github.com/WordPress/gutenberg/pull/56251)) +- DataViews: Rename `InFilter` component to `FilterSummary`. ([56506](https://github.com/WordPress/gutenberg/pull/56506)) +- DataViews: Scope names of V2 UI components. ([56503](https://github.com/WordPress/gutenberg/pull/56503)) +- DataViews: Update field API to generate filters based on type. ([55996](https://github.com/WordPress/gutenberg/pull/55996)) +- DataViews: Update filter component. ([56110](https://github.com/WordPress/gutenberg/pull/56110)) +- Dataviews: Add confirmation step before deleting a page. ([56504](https://github.com/WordPress/gutenberg/pull/56504)) +- Dataviews: Add preview and grid view in templates list. ([56382](https://github.com/WordPress/gutenberg/pull/56382)) +- Dataviews: Grid layout refinements. ([56441](https://github.com/WordPress/gutenberg/pull/56441)) +- Dataviews: Remove link from author. ([56467](https://github.com/WordPress/gutenberg/pull/56467)) +- Dataviews: Update item actions in grid view. ([56501](https://github.com/WordPress/gutenberg/pull/56501)) +- Fix data view menu item radius. ([56395](https://github.com/WordPress/gutenberg/pull/56395)) + +#### Post Editor +- Render html in post titles in visual mode and edit HTML in post title in code view. ([54718](https://github.com/WordPress/gutenberg/pull/54718)) + + +### Documentation + +- Add the attributes definition page to the create block tutorial of the platform documentation. ([56429](https://github.com/WordPress/gutenberg/pull/56429)) +- Add the transforms page to the create block tutorial of the platform documentation. ([56559](https://github.com/WordPress/gutenberg/pull/56559)) +- Add thee block supports page to the create block tutorial of the framework docs. ([56483](https://github.com/WordPress/gutenberg/pull/56483)) +- Added clarifications and examples to "Get started with wp-scripts". ([56298](https://github.com/WordPress/gutenberg/pull/56298)) +- Block Editor: Fix typo in `URLInput`'s `onKeyDown` prop documentation. ([56322](https://github.com/WordPress/gutenberg/pull/56322)) +- Bring back non-JS tabs in block editor handbook. ([56561](https://github.com/WordPress/gutenberg/pull/56561)) +- Docs: Fix incorrect build script description in script package. ([56332](https://github.com/WordPress/gutenberg/pull/56332)) +- Docs: Fundamentals of Block Development - File structure of a block. ([56551](https://github.com/WordPress/gutenberg/pull/56551)) +- Docs: Fundamentals of Block Development - Registration of a block. ([56334](https://github.com/WordPress/gutenberg/pull/56334)) +- Docs: Fundamentals of Block Development - The block wrapper. ([56596](https://github.com/WordPress/gutenberg/pull/56596)) +- Docs: Fundamentals of Block Development - Working with Javascript in the Block Editor. ([56553](https://github.com/WordPress/gutenberg/pull/56553)) +- Docs: Fundamentals of Block Development - block.json. ([56435](https://github.com/WordPress/gutenberg/pull/56435)) +- Docs: Improve downloadBlob example. ([56225](https://github.com/WordPress/gutenberg/pull/56225)) +- Documentation - Block Editor Handbook - Add end user documentation about Block Editor as a resource on the Landing Page of the Block Editor Handbook. ([49854](https://github.com/WordPress/gutenberg/pull/49854)) +- Fix overly complex code example in ComboboxControl readme. ([56365](https://github.com/WordPress/gutenberg/pull/56365)) +- Fix version in useSetting deprecation notice. ([56377](https://github.com/WordPress/gutenberg/pull/56377)) +- Fundamentals block development - landing and first pages. ([56584](https://github.com/WordPress/gutenberg/pull/56584)) +- Fundamentals of Block Development - fix save definition. ([56605](https://github.com/WordPress/gutenberg/pull/56605)) +- Link preview image to live example using WordPress Playground. ([56292](https://github.com/WordPress/gutenberg/pull/56292)) +- NavigableContainers: Fix doc typo in onKeyDown prop. ([56352](https://github.com/WordPress/gutenberg/pull/56352)) +- Release docs: Add new section about troubleshooting the release. ([56436](https://github.com/WordPress/gutenberg/pull/56436)) +- Remove all {% codetabs %} instances and any vanilla JS references. ([56121](https://github.com/WordPress/gutenberg/pull/56121)) +- Simplify code example in ToggleControl component readme. ([56389](https://github.com/WordPress/gutenberg/pull/56389)) +- Text and Heading: Improve documentation around default values and truncation logic. ([56518](https://github.com/WordPress/gutenberg/pull/56518)) +- Theme JSON schema: Add heading/button key to color definition. ([55674](https://github.com/WordPress/gutenberg/pull/55674)) +- Update for 6.4.1 for versions in WP. ([56216](https://github.com/WordPress/gutenberg/pull/56216)) +- Update references to the gutenberg-examples repo to the new block-development-examples. ([56119](https://github.com/WordPress/gutenberg/pull/56119)) +- Update template name in `create-block` command. ([56281](https://github.com/WordPress/gutenberg/pull/56281)) +- Update webpack options for wp-scripts in README.md. ([56314](https://github.com/WordPress/gutenberg/pull/56314)) +- `BoxControl`: Update story and refactor to Typescript. ([56462](https://github.com/WordPress/gutenberg/pull/56462)) + + +### Code Quality + +- Blocks pkg: Remove 'browser' dependencies. ([56433](https://github.com/WordPress/gutenberg/pull/56433)) +- DataViews: Code Quality remove some unused props from action. ([56477](https://github.com/WordPress/gutenberg/pull/56477)) +- Editor: Move the template focus modes to the editor store. ([56472](https://github.com/WordPress/gutenberg/pull/56472)) +- Extract a PostPanelRow component from the different sidebar panels. ([56238](https://github.com/WordPress/gutenberg/pull/56238)) +- Interactivity API: Add missing changelog entry for the new `store()` API. ([56611](https://github.com/WordPress/gutenberg/pull/56611)) +- Migrating block editor `BlockPatternsList` component. ([56210](https://github.com/WordPress/gutenberg/pull/56210)) +- Move the DisableNonContentBlocks component to the editor package. ([56423](https://github.com/WordPress/gutenberg/pull/56423)) +- Post Schedule Panel: Fix Sass deprecation warning for division. ([56412](https://github.com/WordPress/gutenberg/pull/56412)) +- Remove compatibility layer for WP 6.2. ([56464](https://github.com/WordPress/gutenberg/pull/56464)) +- Unify the PostSchedule component between site and post editors. ([56196](https://github.com/WordPress/gutenberg/pull/56196)) +- Update: Refactor useAddedBy to use authorText and originalSource fields. ([56568](https://github.com/WordPress/gutenberg/pull/56568)) + +#### Block Library +- Add align support to the image block - alternative. ([55954](https://github.com/WordPress/gutenberg/pull/55954)) +- Backmerge block renaming fixes/refactors from 6.4 branch into Gutenberg trunk. ([56386](https://github.com/WordPress/gutenberg/pull/56386)) +- Pattern placeholder: Remove duplicate 'useDispatch' hook. ([56397](https://github.com/WordPress/gutenberg/pull/56397)) + +#### Components +- Remove incorrect version from deprecated `__nextHasNoMarginBottom` prop of `AnglePickerControl` Component. ([56336](https://github.com/WordPress/gutenberg/pull/56336)) +- Revert "DropdownMenu V2: Add support for rendering in legacy popover slot". ([56484](https://github.com/WordPress/gutenberg/pull/56484)) + +#### Data Views +- Dataviews: Ensure items and fields are using a unique id. ([56366](https://github.com/WordPress/gutenberg/pull/56366)) + +#### Block Editor +- useInnerBlocksProps: Stabilise dropZoneElement prop. ([56313](https://github.com/WordPress/gutenberg/pull/56313)) + +#### Design Tools +- Fix: Theme.json font settings in unit test. ([56309](https://github.com/WordPress/gutenberg/pull/56309)) + + +### Tools + +- Workflows: Update 'days-before-stale' for flaky test report issues. ([56585](https://github.com/WordPress/gutenberg/pull/56585)) +- scripts: Update `jest-dev-server` to v9. ([56552](https://github.com/WordPress/gutenberg/pull/56552)) + +#### Testing +- Dataviews: Add first end-to-end tests. ([56634](https://github.com/WordPress/gutenberg/pull/56634)) +- Migrate 'align hook' end-to-end tests to Playwright. ([56480](https://github.com/WordPress/gutenberg/pull/56480)) +- Migrate 'block directory' end-to-end tests to Playwright. ([56593](https://github.com/WordPress/gutenberg/pull/56593)) +- Migrate 'block icons' end-to-end tests to Playwright. ([56610](https://github.com/WordPress/gutenberg/pull/56610)) +- Migrate 'custom taxonomies' end-to-end test to Playwright. ([56486](https://github.com/WordPress/gutenberg/pull/56486)) +- Migrate 'sidebar permalink' end-to-end tests to Playwright. ([56253](https://github.com/WordPress/gutenberg/pull/56253)) +- Migrate Is Typing Test to Playwright. ([56616](https://github.com/WordPress/gutenberg/pull/56616)) +- Page spec: Merging create page and toggle preview tests. ([56129](https://github.com/WordPress/gutenberg/pull/56129)) +- Playwright Utils: Fix the method of getting post ID in 'publishPost'. ([56421](https://github.com/WordPress/gutenberg/pull/56421)) +- end-to-end tests: Merge Puppeteer into single job, split Playwright further. ([56363](https://github.com/WordPress/gutenberg/pull/56363)) + +#### Build Tooling +- Create block: Update `interactive-template` to the new `store()` API. ([56613](https://github.com/WordPress/gutenberg/pull/56613)) + + +### Security + +- WP_Theme_JSON_Gutenberg: Add nested indexed array schema sanitization. ([56447](https://github.com/WordPress/gutenberg/pull/56447)) + + +### Various + +- Add: Author text and original source to wp_template_part. ([56567](https://github.com/WordPress/gutenberg/pull/56567)) +- Migrating `BlockPatternSetup` to use updated `Composite` implementation. ([55425](https://github.com/WordPress/gutenberg/pull/55425)) +- Migrating `InserterListbox` to use updated Composite implementation. ([56246](https://github.com/WordPress/gutenberg/pull/56246)) + +#### Data Views +- Dataviews: All Templates: Add filters to template author. ([56338](https://github.com/WordPress/gutenberg/pull/56338)) +- Dataviews: All templates: Add: Sorting to template author and add author_text to the rest API. ([56333](https://github.com/WordPress/gutenberg/pull/56333)) + +#### HTML API +- Backport updates from Core. ([56578](https://github.com/WordPress/gutenberg/pull/56578)) + + + + +## Contributors + +The following contributors merged PRs in this release: + +@aaronrobertshaw @afercia @andrewhayward @andrewserong @annezazu @apeatling @arthur791004 @bph @brookewp @chad1008 @chiilog @ciampo @DAreRodz @dmsnell @draganescu @ellatrix @fabiankaegy @flootr @fluiddot @fullofcaffeine @geriux @getdave @glendaviesnz @jameskoster @jasmussen @jeryj @jffng @jorgefilipecosta @juanmaguitar @kevin940726 @luisherranz @MaggieCabrera @Mamaduka @matiasbenedetto @megane9988 @NekoJonez @ntsekouras @oandregal @ramonjd @richtabor @ryanwelcher @SavPhill @Soean @t-hamano @talldan @tellthemachines @youknowriad @zaguiini + + += 17.1.3 = + + +## Changelog + +### Bug fixes + +#### Components +- https://github.com/WordPress/gutenberg/pull/56570 + + + = 17.1.2 = ## Changelog diff --git a/docs/getting-started/fundamentals/README.md b/docs/getting-started/fundamentals/README.md new file mode 100644 index 00000000000000..26fc88981348b8 --- /dev/null +++ b/docs/getting-started/fundamentals/README.md @@ -0,0 +1,11 @@ +# Fundamentals of Block Development + +This section provides an introduction to the most relevant concepts in Block Development. + +In this section, you will learn: + +1. [**File structure of a block**](https://developer.wordpress.org/block-editor/getting-started/fundamentals/file-structure-of-a-block) - The purpose of each one of the types of files available for a block, the relationships between them, and their role in the output of the block. +1. [**`block.json`**](https://developer.wordpress.org/block-editor/getting-started/fundamentals/block-json) - How a block is defined using its `block.json` metadata and some relevant properties of this file. +1. [**Registration of a block**](https://developer.wordpress.org/block-editor/getting-started/fundamentals/registration-of-a-block) - How a block is registered in both the server and the client. +1. [**Block wrapper**](https://developer.wordpress.org/block-editor/getting-started/fundamentals/block-wrapper) - How to set proper attributes to the block's markup wrapper. +1. [**Javascript in the Block Editor**](https://developer.wordpress.org/block-editor/getting-started/fundamentals/javascript-in-the-block-editor) - How to work with Javascript for the Block Editor. \ No newline at end of file diff --git a/docs/getting-started/fundamentals/block-json.md b/docs/getting-started/fundamentals/block-json.md new file mode 100644 index 00000000000000..3d65a8f016914e --- /dev/null +++ b/docs/getting-started/fundamentals/block-json.md @@ -0,0 +1,115 @@ +# block.json + +The `block.json` file simplifies the processs of defining and registering a block by using the same block's definition in JSON format to register the block in both the server and the client. + +[![Open block.json diagram in excalidraw](https://developer.wordpress.org/files/2023/11/block-json.png)](https://excalidraw.com/#json=v1GrIkGsYGKv8P14irBy6,Yy0vl8q7DTTL2VsH5Ww27A "Open block.json diagram in excalidraw") + +
block.json
+© 2020–2023
+x +``` + +These attributes are passed to the React component `Edit`(to display in the Block Editor) and the `save` function (to return the markup saved to the DB) of the block, and to any server-side render definition for the block (see `render` prop above). + +The `Edit` component receives exclusively the capability of updating the attributes via the `setAttributes` function. + +_See how the attributes are passed to the [`Edit` component](https://github.com/WordPress/block-development-examples/blob/trunk/plugins/copyright-date-block-09aac3/src/edit.js), [the `save` function](https://github.com/WordPress/block-development-examples/blob/trunk/plugins/copyright-date-block-09aac3/src/save.js) and [the `render.php`](https://github.com/WordPress/block-development-examples/blob/trunk/plugins/copyright-date-block-09aac3/src/render.php) in this [full block example](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/copyright-date-block-09aac3) of the code above_ + +attributes
reference page for full info about the Attributes API.
+Hello World
+ +``` + +_See the [full block example](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/block-supports-6aa4dd) of the [code above](https://github.com/WordPress/block-development-examples/blob/trunk/plugins/block-supports-6aa4dd/src/block.json)_ + +supports
reference page for full info about the Supports API.
+supports
generates a set of properties that need to be manually added to the wrapping element of the block so they're properly stored as part of the block data
+Hello World - Block Editor
; + +registerBlockType( ..., { + edit: Edit +} ); +``` +_(see the [code above](https://github.com/WordPress/block-development-examples/blob/trunk/plugins/minimal-block-ca6eda/src/index.js) in [an example](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/minimal-block-ca6eda))_ + +...the markup of the block in the Block Editor could look like this: +```html +Hello World - Block Editor
+``` + +Any additional classes and attributes for the `Edit` component of the block should be passed as an argument of `useBlockProps` (see [example](https://github.com/WordPress/block-development-examples/blob/trunk/plugins/stylesheets-79a4c3/src/edit.js)). When you add `support` for any feature, they get added to the object returned by the `useBlockProps` hook. + + +## The Save component's markup + +When saving the markup in the DB, it’s important to add the block props returned by `useBlockProps.save()` to the wrapper element of your block. `useBlockProps.save()` ensures that the block class name is rendered properly in addition to any HTML attribute injected by the block supports API. + +For example, for the following piece of code of a block's registration in the client that defines the markup desired for the DB (and returned to the front end by default)... + +```js +const Edit = () =>Hello World - Block Editor
; +const save = () =>Hello World - Frontend
; + +registerBlockType( ..., { + edit: Edit, + save, +} ); +``` + +_(see the [code above](https://github.com/WordPress/block-development-examples/blob/trunk/plugins/minimal-block-ca6eda/src/index.js) in [an example](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/minimal-block-ca6eda))_ + + +...the markup of the block in the front end could look like this: +```html +Hello World – Frontend
+``` + +Any additional classes and attributes for the `save` function of the block should be passed as an argument of `useBlockProps.save()` (see [example](https://github.com/WordPress/block-development-examples/blob/trunk/plugins/stylesheets-79a4c3/src/save.js)). + +When you add `support` for any feature, the proper classes get added to the object returned by the `useBlockProps.save()` hook. + +```html +Hello World
+``` + +_(check the [example](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/block-supports-6aa4dd) that generated the HTML above in the front end)_ + +## The server-side render markup + +Any markup in the server-side render definition for the block can use the [`get_block_wrapper_attributes()`](https://developer.wordpress.org/reference/functions/get_block_wrapper_attributes/) function to generate the string of attributes required to reflect the block settings (see [example](https://github.com/WordPress/block-development-examples/blob/f68640f42d993f0866d1879f67c73910285ca114/plugins/block-dynamic-rendering-64756b/src/render.php#L11)). + +```php +> + +
+``` \ No newline at end of file diff --git a/docs/getting-started/fundamentals/file-structure-of-a-block.md b/docs/getting-started/fundamentals/file-structure-of-a-block.md new file mode 100644 index 00000000000000..130483ae5af70f --- /dev/null +++ b/docs/getting-started/fundamentals/file-structure-of-a-block.md @@ -0,0 +1,86 @@ +# File structure of a block + +It is recommended to **register blocks within plugins** to ensure they stay available when a theme gets switched. With the [`create-block` tool](https://developer.wordpress.org/block-editor/getting-started/devenv/get-started-with-create-block/) you can quickly scaffold the structure of the files required to create a plugin that registers a block. + +The files generated by `create-block` are a good reference of the files that can be involved in the definition and registration of a block. + +[![Open File Structure of a Block Diagram in excalidraw](https://developer.wordpress.org/files/2023/11/file-structure-block.png)](https://excalidraw.com/#json=YYpeR-kY1ZMhFKVZxGhMi,mVZewfwNAh_oL-7bj4gmdw "Open File Structure of a Block Diagram in excalidraw") + +### `wp-scripts
includes a css-loader chained with postcss-loader and sass-loader that allows it to process CSS, SASS or SCSS files. Check Default webpack config for more info
+webpack-src-dir
and output-path
option of wp-scripts
build commands to customize the entry and output points
+webpack.config.js
to wp-scripts
to customize the build process to suit your needs
+wp.data.select('core/editor').getBlocks())
in your browser's dev tools while editing a post or a site. The entire editor is available from the console.
+block.json
(or any other .json
file) can be imported directly in Javascript files when using a build process like the one available with wp-scripts
+Hello World - Block Editor
; +const save = () =>Hello World - Frontend
; + +registerBlockType( metadata.name, { + edit: Edit, + save, +} ); +``` +_See the [code above](https://github.com/WordPress/block-development-examples/blob/trunk/plugins/minimal-block-ca6eda/src/index.js) in [an example](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/minimal-block-ca6eda)_ + +## Additional resources + +- [`register_block_type` PHP function](https://developer.wordpress.org/reference/functions/register_block_type/) +- [`registerBlockType` JS function](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-blocks/#registerblocktype) +- [Why a block needs to be registered in both the server and the client?](https://github.com/WordPress/gutenberg/discussions/55884) | GitHub Discussion \ No newline at end of file diff --git a/docs/manifest.json b/docs/manifest.json index 5906743512062c..3ab4cefb2b533c 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -95,6 +95,42 @@ "markdown_source": "../docs/getting-started/create-block/submitting-to-block-directory.md", "parent": "create-block" }, + { + "title": "Fundamentals of Block Development", + "slug": "fundamentals", + "markdown_source": "../docs/getting-started/fundamentals/README.md", + "parent": "getting-started" + }, + { + "title": "File structure of a block", + "slug": "file-structure-of-a-block", + "markdown_source": "../docs/getting-started/fundamentals/file-structure-of-a-block.md", + "parent": "fundamentals" + }, + { + "title": "block.json", + "slug": "block-json", + "markdown_source": "../docs/getting-started/fundamentals/block-json.md", + "parent": "fundamentals" + }, + { + "title": "Registration of a block", + "slug": "registration-of-a-block", + "markdown_source": "../docs/getting-started/fundamentals/registration-of-a-block.md", + "parent": "fundamentals" + }, + { + "title": "The block wrapper", + "slug": "block-wrapper", + "markdown_source": "../docs/getting-started/fundamentals/block-wrapper.md", + "parent": "fundamentals" + }, + { + "title": "Working with Javascript for the Block Editor", + "slug": "javascript-in-the-block-editor", + "markdown_source": "../docs/getting-started/fundamentals/javascript-in-the-block-editor.md", + "parent": "fundamentals" + }, { "title": "Glossary", "slug": "glossary", diff --git a/docs/reference-guides/block-api/block-metadata.md b/docs/reference-guides/block-api/block-metadata.md index f380683f39ccdd..edc61d138128e6 100644 --- a/docs/reference-guides/block-api/block-metadata.md +++ b/docs/reference-guides/block-api/block-metadata.md @@ -77,65 +77,9 @@ Development is improved by using a defined schema definition file. Supported edi "$schema": "https://schemas.wp.org/trunk/block.json" ``` -## Block registration - -### PHP (server-side) - -The [`register_block_type`](https://developer.wordpress.org/reference/functions/register_block_type/) function that aims to simplify the block type registration on the server, can read metadata stored in the `block.json` file. - -This function takes two params relevant in this context (`$block_type` accepts more types and variants): - -- `$block_type` (`string`) – path to the folder where the `block.json` file is located or full path to the metadata file if named differently. -- `$args` (`array`) – an optional array of block type arguments. Default value: `[]`. Any arguments may be defined. However, the one described below is supported by default: - - `$render_callback` (`callable`) – callback used to render blocks of this block type, it's an alternative to the `render` field in `block.json`. - -It returns the registered block type (`WP_Block_Type`) on success or `false` on failure. - -**Example:** - -```php -register_block_type( - __DIR__ . '/notice', - array( - 'render_callback' => 'render_block_core_notice', - ) -); -``` - -### JavaScript (client-side) - -When the block is registered on the server, you only need to register the client-side settings on the client using the same block’s name. - -**Example:** - -```js -registerBlockType( 'my-plugin/notice', { - edit: Edit, - // ...other client-side settings -} ); -``` - -Although registering the block also on the server with PHP is still recommended for the reasons above, if you want to register it only client-side you can now use `registerBlockType` method from `@wordpress/blocks` package to register a block type using the metadata loaded from `block.json` file. - -The function takes two params: - -- `$blockNameOrMetadata` (`string`|`Object`) – block type name (supported previously) or the metadata object loaded from the `block.json` file with a bundler (e.g., webpack) or a custom Babel plugin. -- `$settings` (`Object`) – client-side block settings. - -It returns the registered block type (`WPBlock`) on success or `undefined` on failure. - -**Example:** - -```js -import { registerBlockType } from '@wordpress/blocks'; -import Edit from './edit'; -import metadata from './block.json'; - -registerBlockType( metadata, { - edit: Edit, - // ...other client-side settings -} ); -``` +Mock success response.
', - type: 'rich', - provider_name: 'Twitter', - provider_url: 'https://twitter.com', - version: '1.0', - } ), - }, -]; - -describe( 'Embed block inside a locked all parent', () => { - beforeAll( async () => { - await activatePlugin( 'gutenberg-test-innerblocks-locking-all-embed' ); - } ); - - beforeEach( async () => { - await setUpResponseMocking( MOCK_RESPONSES ); - await createNewPost(); - } ); - - afterAll( async () => { - await deactivatePlugin( - 'gutenberg-test-innerblocks-locking-all-embed' - ); - } ); - - it( 'embed block should be able to embed external content', async () => { - await insertBlock( 'Test Inner Blocks Locking All Embed' ); - const embedInputSelector = - '.components-placeholder__input[aria-label="Embed URL"]'; - await page.waitForSelector( embedInputSelector ); - await page.click( embedInputSelector ); - // This URL should not have a trailing slash. - await page.keyboard.type( 'https://twitter.com/wordpress' ); - await page.keyboard.press( 'Enter' ); - // The twitter block should appear correctly. - await page.waitForSelector( 'figure.wp-block-embed' ); - } ); -} ); diff --git a/packages/e2e-tests/specs/editor/various/is-typing.test.js b/packages/e2e-tests/specs/editor/various/is-typing.test.js deleted file mode 100644 index c6208470ffb8e5..00000000000000 --- a/packages/e2e-tests/specs/editor/various/is-typing.test.js +++ /dev/null @@ -1,101 +0,0 @@ -/** - * WordPress dependencies - */ -import { - clickBlockAppender, - createNewPost, - showBlockToolbar, -} from '@wordpress/e2e-test-utils'; - -describe( 'isTyping', () => { - beforeEach( async () => { - await createNewPost(); - } ); - - it( 'should hide the toolbar when typing', async () => { - const blockToolbarSelector = '.block-editor-block-toolbar'; - - await clickBlockAppender(); - - // Type in a paragraph. - await page.keyboard.type( 'Type' ); - - // Toolbar is hidden - let blockToolbar = await page.$( blockToolbarSelector ); - expect( blockToolbar ).toBe( null ); - - // Moving the mouse shows the toolbar. - await showBlockToolbar(); - - // Toolbar is visible. - blockToolbar = await page.$( blockToolbarSelector ); - expect( blockToolbar ).not.toBe( null ); - - // Typing again hides the toolbar - await page.keyboard.type( ' and continue' ); - - // Toolbar is hidden again - blockToolbar = await page.$( blockToolbarSelector ); - expect( blockToolbar ).toBe( null ); - } ); - - it( 'should not close the dropdown when typing in it', async () => { - // Adds a Dropdown with an input to all blocks. - await page.evaluate( () => { - const { Dropdown, ToolbarButton, Fill } = wp.components; - const { createElement: el, Fragment } = wp.element; - function AddDropdown( BlockListBlock ) { - return ( props ) => { - return el( - Fragment, - {}, - el( - Fill, - { name: 'BlockControls' }, - el( Dropdown, { - renderToggle: ( { onToggle } ) => - el( - ToolbarButton, - { - onClick: onToggle, - className: 'dropdown-open', - }, - 'Open Dropdown' - ), - renderContent: () => - el( 'input', { - className: 'dropdown-input', - } ), - } ) - ), - el( BlockListBlock, props ) - ); - }; - } - - wp.hooks.addFilter( - 'editor.BlockListBlock', - 'e2e-test/add-dropdown', - AddDropdown - ); - } ); - - await clickBlockAppender(); - - // Type in a paragraph. - await page.keyboard.type( 'Type' ); - - // Show Toolbar. - await showBlockToolbar(); - - // Open the dropdown. - await page.click( '.dropdown-open' ); - - // Type inside the dropdown's input - await page.type( '.dropdown-input', 'Random' ); - - // The input should still be visible. - const input = await page.$( '.dropdown-input' ); - expect( input ).not.toBe( null ); - } ); -} ); diff --git a/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js b/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js deleted file mode 100644 index fe5334d2a12769..00000000000000 --- a/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js +++ /dev/null @@ -1,239 +0,0 @@ -/** - * WordPress dependencies - */ -import { - createNewPost, - disablePrePublishChecks, - getOption, - insertBlock, - publishPost, - setOption, - trashAllPosts, - activateTheme, - clickButton, - createReusableBlock, - deleteAllTemplates, - canvas, -} from '@wordpress/e2e-test-utils'; - -describe( 'Multi-entity save flow', () => { - // Selectors - usable between Post/Site editors. - const checkedBoxSelector = '.components-checkbox-control__checked'; - const checkboxInputSelector = '.components-checkbox-control__input'; - const entitiesSaveSelector = '.editor-entities-saved-states__save-button'; - const savePanelSelector = '.entities-saved-states__panel'; - const closePanelButtonSelector = - '.editor-post-publish-panel__header-cancel-button button:not(:disabled)'; - - // Reusable assertions across Post/Site editors. - const assertAllBoxesChecked = async () => { - const checkedBoxes = await page.$$( checkedBoxSelector ); - const checkboxInputs = await page.$$( checkboxInputSelector ); - expect( checkedBoxes.length - checkboxInputs.length ).toBe( 0 ); - }; - const assertExistence = async ( selector, shouldBePresent ) => { - const element = await page.$( selector ); - if ( shouldBePresent ) { - expect( element ).not.toBeNull(); - } else { - expect( element ).toBeNull(); - } - }; - - let originalSiteTitle, originalBlogDescription; - - beforeAll( async () => { - await activateTheme( 'emptytheme' ); - await deleteAllTemplates( 'wp_template' ); - await deleteAllTemplates( 'wp_template_part' ); - await trashAllPosts( 'wp_block' ); - - // Get the current Site Title and Site Tagline, so that we can reset - // them back to the original values once the test suite has finished. - originalSiteTitle = await getOption( 'blogname' ); - originalBlogDescription = await getOption( 'blogdescription' ); - } ); - - afterAll( async () => { - await activateTheme( 'twentytwentyone' ); - - // Reset the Site Title and Site Tagline back to their original values. - await setOption( 'blogname', originalSiteTitle ); - await setOption( 'blogdescription', originalBlogDescription ); - } ); - - describe( 'Post Editor', () => { - // Selectors - Post editor specific. - const draftSavedSelector = '.editor-post-saved-state.is-saved'; - const multiSaveSelector = - '.editor-post-publish-button__button.has-changes-dot'; - const savePostSelector = '.editor-post-publish-button__button'; - const enabledSavePostSelector = `${ savePostSelector }[aria-disabled=false]`; - const publishA11ySelector = - '.edit-post-layout__toggle-publish-panel-button'; - const saveA11ySelector = - '.edit-post-layout__toggle-entities-saved-states-panel-button'; - const publishPanelSelector = '.editor-post-publish-panel'; - - // Reusable assertions inside Post editor. - const assertMultiSaveEnabled = async () => { - const multiSaveButton = - await page.waitForSelector( multiSaveSelector ); - expect( multiSaveButton ).not.toBeNull(); - }; - const assertMultiSaveDisabled = async () => { - const multiSaveButton = await page.waitForSelector( - multiSaveSelector, - { hidden: true } - ); - expect( multiSaveButton ).toBeNull(); - }; - - it( 'Save flow should work as expected.', async () => { - await createNewPost(); - // Edit the page some. - await canvas().waitForSelector( '.editor-post-title' ); - await canvas().click( '.editor-post-title' ); - await page.keyboard.type( 'Test Post...' ); - await page.keyboard.press( 'Enter' ); - - // Should not trigger multi-entity save button with only post edited. - await assertMultiSaveDisabled(); - - // Should only have publish panel a11y button active with only post edited. - await assertExistence( publishA11ySelector, true ); - await assertExistence( saveA11ySelector, false ); - await assertExistence( publishPanelSelector, false ); - await assertExistence( savePanelSelector, false ); - - // Add a reusable block and edit it. - await createReusableBlock( 'Hi!', 'Test' ); - await canvas().waitForSelector( 'p[data-type="core/paragraph"]' ); - await canvas().click( 'p[data-type="core/paragraph"]' ); - await page.keyboard.type( 'Oh!' ); - - // Should trigger multi-entity save button once template part edited. - await assertMultiSaveEnabled(); - - // Should only have save panel a11y button active after child entities edited. - await assertExistence( publishA11ySelector, false ); - await assertExistence( saveA11ySelector, true ); - await assertExistence( publishPanelSelector, false ); - await assertExistence( savePanelSelector, false ); - - // Opening panel has boxes checked by default. - await page.click( savePostSelector ); - await page.waitForSelector( savePanelSelector ); - await assertAllBoxesChecked(); - - // Should not show other panels (or their a11y buttons) while save panel opened. - await assertExistence( publishA11ySelector, false ); - await assertExistence( saveA11ySelector, false ); - await assertExistence( publishPanelSelector, false ); - - // Publish panel should open after saving. - await page.click( entitiesSaveSelector ); - await page.waitForSelector( publishPanelSelector ); - - // No other panels (or their a11y buttons) should be present with publish panel open. - await assertExistence( publishA11ySelector, false ); - await assertExistence( saveA11ySelector, false ); - await assertExistence( savePanelSelector, false ); - - // Close publish panel. - const closePanelButton = await page.waitForSelector( - closePanelButtonSelector - ); - await closePanelButton.click(); - - // Verify saving is disabled. - const draftSaved = await page.waitForSelector( draftSavedSelector ); - expect( draftSaved ).not.toBeNull(); - await assertMultiSaveDisabled(); - await assertExistence( saveA11ySelector, false ); - - await publishPost(); - // Wait for the success notice specifically for the published post. - // `publishPost()` has a similar check but it only checks for the - // existence of any snackbars. In this case, there's another "Site updated" - // notice which will be sufficient for that and thus creating a false-positive. - await page.waitForXPath( - '//*[@id="a11y-speak-polite"][contains(text(), "Post published")]' - ); - - // Unselect the blocks to avoid clicking the block toolbar. - await page.evaluate( () => { - wp.data.dispatch( 'core/block-editor' ).clearSelectedBlock(); - } ); - - // Update the post. - await canvas().click( '.editor-post-title' ); - await page.keyboard.type( '...more title!' ); - - // Verify update button is enabled. - const enabledSaveButton = await page.waitForSelector( - enabledSavePostSelector - ); - expect( enabledSaveButton ).not.toBeNull(); - // Verify multi-entity saving not enabled. - await assertMultiSaveDisabled(); - await assertExistence( saveA11ySelector, false ); - - // Update reusable block again. - await canvas().click( 'p[data-type="core/paragraph"]' ); - // We need to click again due to the clickthrough overlays in reusable blocks. - await canvas().click( 'p[data-type="core/paragraph"]' ); - await page.keyboard.type( 'R!' ); - - // Multi-entity saving should be enabled. - await assertMultiSaveEnabled(); - await assertExistence( saveA11ySelector, true ); - } ); - - it( 'Site blocks should save individually', async () => { - await createNewPost(); - await disablePrePublishChecks(); - - await insertBlock( 'Site Title' ); - // Ensure title is retrieved before typing. - await page.waitForXPath( - `//a[contains(text(), "${ originalSiteTitle }")]` - ); - const editableSiteTitleSelector = - '.wp-block-site-title a[contenteditable="true"]'; - await canvas().waitForSelector( editableSiteTitleSelector ); - await canvas().focus( editableSiteTitleSelector ); - await page.keyboard.type( '...' ); - - await insertBlock( 'Site Tagline' ); - // Wait for the placeholder. - await canvas().waitForXPath( - '//span[@data-rich-text-placeholder="Write site tagline…"]' - ); - const editableSiteTagLineSelector = - '.wp-block-site-tagline[contenteditable="true"]'; - await canvas().waitForSelector( editableSiteTagLineSelector ); - await canvas().focus( editableSiteTagLineSelector ); - await page.keyboard.type( 'Just another WordPress site' ); - - await clickButton( 'Publish' ); - await page.waitForSelector( savePanelSelector ); - let checkboxInputs = await page.$$( checkboxInputSelector ); - expect( checkboxInputs ).toHaveLength( 3 ); - - await checkboxInputs[ 1 ].click(); - await page.click( entitiesSaveSelector ); - - // Wait for the snackbar notice that the post has been published. - await page.waitForSelector( '.components-snackbar' ); - - await clickButton( 'Update…' ); - await page.waitForSelector( savePanelSelector ); - - await page.waitForSelector( checkboxInputSelector ); - checkboxInputs = await page.$$( checkboxInputSelector ); - - expect( checkboxInputs ).toHaveLength( 1 ); - } ); - } ); -} ); diff --git a/packages/e2e-tests/specs/site-editor/site-editor-export.test.js b/packages/e2e-tests/specs/site-editor/site-editor-export.test.js deleted file mode 100644 index 0e560e9b7e0ade..00000000000000 --- a/packages/e2e-tests/specs/site-editor/site-editor-export.test.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * External dependencies - */ -import fs from 'fs'; -import path from 'path'; -import os from 'os'; - -/** - * WordPress dependencies - */ -import { - deleteAllTemplates, - activateTheme, - visitSiteEditor, - enterEditMode, - clickOnMoreMenuItem, -} from '@wordpress/e2e-test-utils'; - -async function waitForFileExists( filePath, timeout = 10000 ) { - const start = Date.now(); - while ( ! fs.existsSync( filePath ) ) { - // Puppeteer doesn't have an API for managing file downloads. - // We are using `waitForTimeout` to add delays between check of file existence. - // eslint-disable-next-line no-restricted-syntax - await page.waitForTimeout( 1000 ); - if ( Date.now() - start > timeout ) { - throw Error( 'waitForFileExists timeout' ); - } - } -} - -describe( 'Site Editor Templates Export', () => { - beforeAll( async () => { - await activateTheme( 'emptytheme' ); - await deleteAllTemplates( 'wp_template' ); - await deleteAllTemplates( 'wp_template_part' ); - } ); - - afterAll( async () => { - await activateTheme( 'twentytwentyone' ); - } ); - - beforeEach( async () => { - await visitSiteEditor(); - await enterEditMode(); - } ); - - it( 'clicking export should download emptytheme.zip file', async () => { - const directory = fs.mkdtempSync( - path.join( os.tmpdir(), 'test-edit-site-export-' ) - ); - await page._client.send( 'Page.setDownloadBehavior', { - behavior: 'allow', - downloadPath: directory, - } ); - - await clickOnMoreMenuItem( 'Export', 'site-editor' ); - const filePath = path.join( directory, 'emptytheme.zip' ); - await waitForFileExists( filePath ); - expect( fs.existsSync( filePath ) ).toBe( true ); - fs.unlinkSync( filePath ); - } ); -} ); diff --git a/packages/edit-post/CHANGELOG.md b/packages/edit-post/CHANGELOG.md index e8e0fa1632a7da..70c562e812f1ea 100644 --- a/packages/edit-post/CHANGELOG.md +++ b/packages/edit-post/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 7.24.0 (2023-11-29) + ## 7.23.0 (2023-11-16) ## 7.22.0 (2023-11-02) diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index 31c5c209fa96c2..0bc4376cedec94 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-post", - "version": "7.23.0", + "version": "7.24.0", "description": "Edit Post module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-post/src/components/text-editor/style.scss b/packages/edit-post/src/components/text-editor/style.scss index 925e88df27180b..c02e983057e6ef 100644 --- a/packages/edit-post/src/components/text-editor/style.scss +++ b/packages/edit-post/src/components/text-editor/style.scss @@ -5,7 +5,8 @@ flex-grow: 1; // Post title. - .editor-post-title { + .editor-post-title:not(.is-raw-text), + .editor-post-title.is-raw-text textarea { max-width: none; line-height: $default-line-height; @@ -14,6 +15,7 @@ font-weight: normal; border: $border-width solid $gray-600; + border-radius: 0; // Same padding as body. padding: $grid-unit-20; diff --git a/packages/edit-site/CHANGELOG.md b/packages/edit-site/CHANGELOG.md index b8814356b2b6b4..de75adec15e5a9 100644 --- a/packages/edit-site/CHANGELOG.md +++ b/packages/edit-site/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 5.24.0 (2023-11-29) + ## 5.23.0 (2023-11-16) ## 5.22.0 (2023-11-02) diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 072dc0b1c027a8..2ff4f10c084a88 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/edit-site", - "version": "5.23.0", + "version": "5.24.0", "description": "Edit Site Page module for WordPress.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/packages/edit-site/src/components/actions/index.js b/packages/edit-site/src/components/actions/index.js index c90ffde135a01d..ca673e3867bdaf 100644 --- a/packages/edit-site/src/components/actions/index.js +++ b/packages/edit-site/src/components/actions/index.js @@ -10,6 +10,13 @@ import { __, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { useMemo } from '@wordpress/element'; import { privateApis as routerPrivateApis } from '@wordpress/router'; +import { + Button, + __experimentalText as Text, + __experimentalHStack as HStack, + __experimentalVStack as VStack, +} from '@wordpress/components'; + /** * Internal dependencies */ @@ -17,55 +24,76 @@ import { unlock } from '../../lock-unlock'; const { useHistory } = unlock( routerPrivateApis ); -export function useTrashPostAction() { - const { createSuccessNotice, createErrorNotice } = - useDispatch( noticesStore ); - const { deleteEntityRecord } = useDispatch( coreStore ); - - return useMemo( - () => ( { - id: 'move-to-trash', - label: __( 'Move to Trash' ), - isPrimary: true, - icon: trash, - isEligible( { status } ) { - return status !== 'trash'; - }, - async callback( post ) { - try { - await deleteEntityRecord( - 'postType', - post.type, - post.id, - {}, - { throwOnError: true } - ); - createSuccessNotice( - sprintf( - /* translators: The page's title. */ - __( '"%s" moved to the Trash.' ), - decodeEntities( post.title.rendered ) - ), - { - type: 'snackbar', - id: 'edit-site-page-trashed', - } - ); - } catch ( error ) { - const errorMessage = - error.message && error.code !== 'unknown_error' - ? error.message - : __( - 'An error occurred while moving the page to the trash.' - ); +export const trashPostAction = { + id: 'move-to-trash', + label: __( 'Move to Trash' ), + isPrimary: true, + icon: trash, + isEligible( { status } ) { + return status !== 'trash'; + }, + hideModalHeader: true, + RenderModal: ( { item: post, closeModal } ) => { + const { createSuccessNotice, createErrorNotice } = + useDispatch( noticesStore ); + const { deleteEntityRecord } = useDispatch( coreStore ); + return ( +Mock success response.
', + type: 'rich', + provider_name: 'Twitter', + provider_url: 'https://twitter.com', + version: '1.0', +}; + +test.describe( 'Embed block inside a locked all parent', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activatePlugin( + 'gutenberg-test-innerblocks-locking-all-embed' + ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.deactivatePlugin( + 'gutenberg-test-innerblocks-locking-all-embed' + ); + } ); + + test( 'embed block should be able to embed external content', async ( { + admin, + editor, + page, + } ) => { + await admin.createNewPost(); + await page.route( + ( url ) => EMBED_URLS.some( ( u ) => url.href.includes( u ) ), + async ( route ) => { + await route.fulfill( { + json: MOCK_RESPONSES, + } ); + } + ); + + await editor.insertBlock( { + name: 'test/test-inner-blocks-locking-all-embed', + } ); + await page + .getByRole( 'textbox', { name: 'Embed URL' } ) + .fill( 'https://twitter.com/wordpress' ); + await page.keyboard.press( 'Enter' ); + + await expect( + page.getByRole( 'document', { name: 'Block: Twitter' } ) + ).toBeVisible(); + } ); +} ); diff --git a/test/e2e/specs/editor/various/is-typing.spec.js b/test/e2e/specs/editor/various/is-typing.spec.js new file mode 100644 index 00000000000000..0cd5e0d6f64953 --- /dev/null +++ b/test/e2e/specs/editor/various/is-typing.spec.js @@ -0,0 +1,63 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'isTyping', () => { + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test( 'should hide the toolbar when typing', async ( { editor, page } ) => { + // Enter to reach paragraph block. + await page.keyboard.press( 'Enter' ); + // Insert paragraph + await page.keyboard.type( 'Type' ); + + const blockToolbar = page.locator( + 'role=toolbar[name="Block tools"i]' + ); + + // Toolbar should not be showing + await expect( blockToolbar ).toBeHidden(); + + // Moving the mouse shows the toolbar. + await editor.showBlockToolbar(); + + // Toolbar is visible. + await expect( blockToolbar ).toBeVisible(); + + // Typing again hides the toolbar + await page.keyboard.type( ' and continue' ); + + // Toolbar is hidden again + await expect( blockToolbar ).toBeHidden(); + } ); + + test( 'should not close the dropdown when typing in it', async ( { + editor, + page, + } ) => { + // Add a block with a dropdown in the toolbar that contains an input. + await editor.insertBlock( { name: 'core/query' } ); + + // Tab to Start Blank Button + await page.keyboard.press( 'Tab' ); + // Select the Start Blank Button + await page.keyboard.press( 'Enter' ); + // Select the First variation + await page.keyboard.press( 'Enter' ); + // Moving the mouse shows the toolbar. + await editor.showBlockToolbar(); + // Open the dropdown. + await page.getByRole( 'button', { name: 'Display settings' } ).click(); + + const itemsPerPageInput = page.getByLabel( 'Items per Page' ); + // Make sure we're where we think we are + await expect( itemsPerPageInput ).toBeFocused(); + // Type inside the dropdown's input + await page.keyboard.type( '00' ); + // The input should still be visible. + await expect( itemsPerPageInput ).toBeVisible(); + } ); +} ); diff --git a/test/e2e/specs/editor/various/multi-entity-saving.spec.js b/test/e2e/specs/editor/various/multi-entity-saving.spec.js new file mode 100644 index 00000000000000..7a7298c137c4b5 --- /dev/null +++ b/test/e2e/specs/editor/various/multi-entity-saving.spec.js @@ -0,0 +1,210 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Editor - Multi-entity save flow', () => { + let originalSiteTitle, originalBlogDescription; + + test.beforeEach( async ( { requestUtils } ) => { + const siteSettings = await requestUtils.getSiteSettings(); + + originalSiteTitle = siteSettings.title; + originalBlogDescription = siteSettings.description; + } ); + + test.afterEach( async ( { requestUtils, editor } ) => { + await requestUtils.updateSiteSettings( { + title: originalSiteTitle, + description: originalBlogDescription, + } ); + + // Restore the Publish sidebar. + await editor.setPreferences( 'core/edit-post', { + isPublishSidebarEnabled: true, + } ); + } ); + + test( 'Save flow should work as expected', async ( { + admin, + editor, + page, + } ) => { + await admin.createNewPost(); + await editor.canvas + .getByRole( 'textbox', { name: 'Add title' } ) + .fill( 'Test Post...' ); + await page.keyboard.press( 'Enter' ); + + const topBar = page.getByRole( 'region', { name: 'Editor top bar' } ); + const publishButton = topBar.getByRole( 'button', { name: 'Publish' } ); + + // Should not trigger multi-entity save button with only post edited. + await expect( publishButton ).toBeEnabled(); + await expect( publishButton ).not.toHaveClass( /has-changes-dot/ ); + + const openPublishPanel = page.getByRole( 'button', { + name: 'Open publish panel', + } ); + const openSavePanel = page.getByRole( 'button', { + name: 'Open save panel', + } ); + const publishPanel = page.getByRole( 'region', { + name: 'Editor publish', + } ); + + // Should only have publish panel a11y button active with only post edited. + await expect( openPublishPanel ).toBeVisible(); + await expect( openSavePanel ).toBeHidden(); + await expect( publishPanel ).not.toContainText( + 'Are you ready to publish?' + ); + await expect( publishPanel ).not.toContainText( + 'Are you ready to save?' + ); + + // Add a title block and edit it. + await editor.insertBlock( { + name: 'core/site-title', + } ); + const siteTitleField = editor.canvas.getByRole( 'textbox', { + name: 'Site title text', + } ); + await siteTitleField.fill( `${ originalSiteTitle }...` ); + + // Should trigger multi-entity save button once template part edited. + await expect( publishButton ).toHaveClass( /has-changes-dot/ ); + + // Should only have save panel a11y button active after child entities edited. + await expect( openPublishPanel ).toBeHidden(); + await expect( openSavePanel ).toBeVisible(); + await expect( publishPanel ).not.toContainText( + 'Are you ready to publish?' + ); + await expect( publishPanel ).not.toContainText( + 'Are you ready to save?' + ); + + // Opening panel has boxes checked by default. + await publishButton.click(); + await expect( publishPanel ).toContainText( 'Are you ready to save?' ); + const allCheckboxes = await publishPanel + .getByRole( 'checkbox' ) + .count(); + await expect( + publishPanel.getByRole( 'checkbox', { checked: true } ) + ).toHaveCount( allCheckboxes ); + + // Should not show other panels (or their a11y buttons) while save panel opened. + await expect( openPublishPanel ).toBeHidden(); + await expect( openSavePanel ).toBeHidden(); + await expect( publishPanel ).not.toContainText( + 'Are you ready to publish?' + ); + + // Publish panel should open after saving. + await publishPanel.getByRole( 'button', { name: 'Save' } ).click(); + await expect( publishPanel ).toContainText( + 'Are you ready to publish?' + ); + + // No other panels (or their a11y buttons) should be present with publish panel open. + await expect( openPublishPanel ).toBeHidden(); + await expect( openSavePanel ).toBeHidden(); + await expect( publishPanel ).not.toContainText( + 'Are you ready to save?' + ); + + // Close publish panel. + await publishPanel.getByRole( 'button', { name: 'Cancel' } ).click(); + + // Verify saving is disabled. + await expect( + topBar.getByRole( 'button', { name: 'Saved' } ) + ).toBeDisabled(); + await expect( publishButton ).not.toHaveClass( /has-changes-dot/ ); + await expect( openSavePanel ).toBeHidden(); + + await editor.publishPost(); + + // Update the post. + await editor.canvas + .getByRole( 'textbox', { name: 'Add title' } ) + .fill( 'Updated post title' ); + + const updateButton = topBar.getByRole( 'button', { name: 'Update' } ); + + // Verify update button is enabled. + await expect( updateButton ).toBeEnabled(); + + // Verify multi-entity saving not enabled. + await expect( updateButton ).not.toHaveClass( /has-changes-dot/ ); + await expect( openSavePanel ).toBeHidden(); + + await siteTitleField.fill( `${ originalSiteTitle }!` ); + + // Multi-entity saving should be enabled. + await expect( updateButton ).toHaveClass( /has-changes-dot/ ); + await expect( openSavePanel ).toBeVisible(); + } ); + + test( 'Site blocks should save individually', async ( { + admin, + editor, + page, + } ) => { + await admin.createNewPost(); + await editor.setPreferences( 'core/edit-post', { + isPublishSidebarEnabled: false, + } ); + + // Add site blocks. + await editor.insertBlock( { + name: 'core/site-title', + } ); + await editor.insertBlock( { + name: 'core/site-tagline', + } ); + + const siteTitleField = editor.canvas.getByRole( 'textbox', { + name: 'Site title text', + } ); + + // Ensure title is retrieved before typing. + await expect( siteTitleField ).toHaveText( originalSiteTitle ); + + await siteTitleField.fill( `${ originalSiteTitle }...` ); + await editor.canvas + .getByRole( 'document', { + name: 'Block: Site Tagline', + } ) + .fill( 'Just another WordPress site' ); + + const topBar = page.getByRole( 'region', { name: 'Editor top bar' } ); + const publishPanel = page.getByRole( 'region', { + name: 'Editor publish', + } ); + + await topBar.getByRole( 'button', { name: 'Publish' } ).click(); + await expect( publishPanel.getByRole( 'checkbox' ) ).toHaveCount( 3 ); + + // Skip site title saving. + await publishPanel + .getByRole( 'checkbox', { + name: 'Title', + } ) + .setChecked( false ); + + await publishPanel.getByRole( 'button', { name: 'Save' } ).click(); + + // Wait for the snackbar notice that the post has been published. + await page + .getByRole( 'button', { name: 'Dismiss this notice' } ) + .filter( { hasText: 'published' } ) + .waitFor(); + + await topBar.getByRole( 'button', { name: 'Update' } ).click(); + + await expect( publishPanel.getByRole( 'checkbox' ) ).toHaveCount( 1 ); + } ); +} ); diff --git a/test/e2e/specs/interactivity/directive-effect.spec.ts b/test/e2e/specs/interactivity/directive-watch.spec.ts similarity index 79% rename from test/e2e/specs/interactivity/directive-effect.spec.ts rename to test/e2e/specs/interactivity/directive-watch.spec.ts index 40030d257661fc..09bd0214c0a51e 100644 --- a/test/e2e/specs/interactivity/directive-effect.spec.ts +++ b/test/e2e/specs/interactivity/directive-watch.spec.ts @@ -3,14 +3,14 @@ */ import { test, expect } from './fixtures'; -test.describe( 'data-wp-effect', () => { +test.describe( 'data-wp-watch', () => { test.beforeAll( async ( { interactivityUtils: utils } ) => { await utils.activatePlugins(); - await utils.addPostWithBlock( 'test/directive-effect' ); + await utils.addPostWithBlock( 'test/directive-watch' ); } ); test.beforeEach( async ( { interactivityUtils: utils, page } ) => { - await page.goto( utils.getLink( 'test/directive-effect' ) ); + await page.goto( utils.getLink( 'test/directive-watch' ) ); } ); test.afterAll( async ( { interactivityUtils: utils } ) => { @@ -18,12 +18,12 @@ test.describe( 'data-wp-effect', () => { await utils.deleteAllPosts(); } ); - test( 'check that effect runs when it is added', async ( { page } ) => { + test( 'check that watch runs when it is added', async ( { page } ) => { const el = page.getByTestId( 'element in the DOM' ); await expect( el ).toContainText( 'element is in the DOM' ); } ); - test( 'check that effect runs when it is removed', async ( { page } ) => { + test( 'check that watch runs when it is removed', async ( { page } ) => { await page.getByTestId( 'toggle' ).click(); const el = page.getByTestId( 'element in the DOM' ); await expect( el ).toContainText( 'element is not in the DOM' ); diff --git a/test/e2e/specs/interactivity/store-afterload.spec.ts b/test/e2e/specs/interactivity/store-afterload.spec.ts deleted file mode 100644 index 388e80177b0339..00000000000000 --- a/test/e2e/specs/interactivity/store-afterload.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Internal dependencies - */ -import { test, expect } from './fixtures'; - -test.describe( 'store afterLoad callbacks', () => { - test.beforeAll( async ( { interactivityUtils: utils } ) => { - await utils.activatePlugins(); - await utils.addPostWithBlock( 'test/store-afterload' ); - } ); - - test.beforeEach( async ( { interactivityUtils: utils, page } ) => { - await page.goto( utils.getLink( 'test/store-afterload' ) ); - } ); - - test.afterAll( async ( { interactivityUtils: utils } ) => { - await utils.deactivatePlugins(); - await utils.deleteAllPosts(); - } ); - - test( 'run after the vdom and store are ready', async ( { page } ) => { - const allStoresReady = page.getByTestId( 'all-stores-ready' ); - const vdomReady = page.getByTestId( 'vdom-ready' ); - - await expect( allStoresReady ).toHaveText( 'true' ); - await expect( vdomReady ).toHaveText( 'true' ); - } ); - - test( 'run once even if shared between several store calls', async ( { - page, - } ) => { - const afterLoadTimes = page.getByTestId( 'after-load-exec-times' ); - const sharedAfterLoadTimes = page.getByTestId( - 'shared-after-load-exec-times' - ); - - await expect( afterLoadTimes ).toHaveText( '1' ); - await expect( sharedAfterLoadTimes ).toHaveText( '1' ); - } ); -} ); diff --git a/test/e2e/specs/site-editor/new-templates-list.spec.js b/test/e2e/specs/site-editor/new-templates-list.spec.js new file mode 100644 index 00000000000000..be6008080200c8 --- /dev/null +++ b/test/e2e/specs/site-editor/new-templates-list.spec.js @@ -0,0 +1,86 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Templates', () => { + test.beforeAll( async ( { requestUtils } ) => { + await Promise.all( [ + requestUtils.activateTheme( 'emptytheme' ), + requestUtils.activatePlugin( 'gutenberg-test-dataviews' ), + ] ); + } ); + test.afterAll( async ( { requestUtils } ) => { + await Promise.all( [ + requestUtils.activateTheme( 'twentytwentyone' ), + requestUtils.deactivatePlugin( 'gutenberg-test-dataviews' ), + ] ); + } ); + test( 'Sorting', async ( { admin, page } ) => { + await admin.visitSiteEditor( { path: '/wp_template/all' } ); + // Descending by title. + await page.getByRole( 'button', { name: 'Template' } ).click(); + await page.getByRole( 'menuitem', { name: 'Sort descending' } ).click(); + const firstTitle = page + .getByRole( 'region', { + name: 'Template', + includeHidden: true, + } ) + .getByRole( 'heading', { + level: 3, + includeHidden: true, + } ) + .first(); + await expect( firstTitle ).toHaveText( 'Tag Archives' ); + // Ascending by title. + await page.getByRole( 'menuitem', { name: 'Sort ascending' } ).click(); + await expect( firstTitle ).toHaveText( 'Category Archives' ); + } ); + test( 'Filtering', async ( { requestUtils, admin, page } ) => { + await requestUtils.createTemplate( 'wp_template', { + slug: 'date', + title: 'Date Archives', + content: 'hi', + } ); + await admin.visitSiteEditor( { path: '/wp_template/all' } ); + // Global search. + await page.getByRole( 'searchbox', { name: 'Filter list' } ).click(); + await page.keyboard.type( 'tag' ); + const titles = page + .getByRole( 'region', { name: 'Template' } ) + .getByRole( 'heading', { level: 3 } ); + await expect( titles ).toHaveCount( 1 ); + await expect( titles.first() ).toHaveText( 'Tag Archives' ); + await page.getByRole( 'button', { name: 'Reset filters' } ).click(); + await expect( titles ).toHaveCount( 6 ); + + // Filter by author. + await page.getByRole( 'button', { name: 'Add filter' } ).click(); + await page.getByRole( 'menuitem', { name: 'Author' } ).hover(); + await page.getByRole( 'menuitemcheckbox', { name: 'admin' } ).click(); + await expect( titles ).toHaveCount( 1 ); + await expect( titles.first() ).toHaveText( 'Date Archives' ); + + // Filter by author and text. + await page.getByRole( 'button', { name: 'Reset filters' } ).click(); + await page.getByRole( 'searchbox', { name: 'Filter list' } ).click(); + await page.keyboard.type( 'archives' ); + await expect( titles ).toHaveCount( 3 ); + await page.getByRole( 'button', { name: 'Add filter' } ).click(); + await page.getByRole( 'menuitem', { name: 'Author' } ).hover(); + await page + .getByRole( 'menuitemcheckbox', { name: 'Emptytheme' } ) + .click(); + await expect( titles ).toHaveCount( 2 ); + + await requestUtils.deleteAllTemplates( 'wp_template' ); + } ); + test( 'Field visibility', async ( { admin, page } ) => { + await admin.visitSiteEditor( { path: '/wp_template/all' } ); + await page.getByRole( 'button', { name: 'Description' } ).click(); + await page.getByRole( 'menuitem', { name: 'Hide' } ).click(); + await expect( + page.getByRole( 'button', { name: 'Description' } ) + ).toBeHidden(); + } ); +} ); diff --git a/test/e2e/specs/site-editor/site-editor-export.spec.js b/test/e2e/specs/site-editor/site-editor-export.spec.js new file mode 100644 index 00000000000000..a0a56c18089cc2 --- /dev/null +++ b/test/e2e/specs/site-editor/site-editor-export.spec.js @@ -0,0 +1,38 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Site Editor Templates Export', () => { + test.beforeAll( async ( { requestUtils } ) => { + await Promise.all( [ + requestUtils.activateTheme( 'emptytheme' ), + requestUtils.deleteAllTemplates( 'wp_template' ), + requestUtils.deleteAllTemplates( 'wp_template_part' ), + ] ); + } ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyone' ); + } ); + + test( 'clicking export should download emptytheme.zip file', async ( { + admin, + page, + } ) => { + await admin.visitSiteEditor( { + postId: 'emptytheme//index', + postType: 'wp_template', + canvas: 'edit', + } ); + await page + .getByRole( 'region', { name: 'Editor top bar' } ) + .getByRole( 'button', { name: 'Options' } ) + .click(); + + const promise = page.waitForEvent( 'download' ); + await page.getByRole( 'menuitem', { name: 'Export' } ).click(); + const download = await promise; + expect( download.suggestedFilename() ).toBe( 'emptytheme.zip' ); + } ); +} ); diff --git a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js index 61fbf7c795a60b..2d51b5ac5014b8 100644 --- a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js +++ b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js @@ -147,9 +147,6 @@ class UserGlobalStylesRevisions { .getByRole( 'menubar', { name: 'Styles actions' } ) .click(); await this.page.getByRole( 'button', { name: 'Revisions' } ).click(); - await this.page - .getByRole( 'menuitem', { name: /^Revision history/ } ) - .click(); } async openStylesPanel() { diff --git a/test/integration/blocks-raw-handling.test.js b/test/integration/blocks-raw-handling.test.js index 229fa0ba7761c8..8acfb052436ed7 100644 --- a/test/integration/blocks-raw-handling.test.js +++ b/test/integration/blocks-raw-handling.test.js @@ -369,6 +369,34 @@ describe( 'Blocks raw handling', () => { expect( console ).toHaveLogged(); } ); + it( 'should convert pre', () => { + const transformed = pasteHandler( { + HTML: '1\n2', + plainText: '1\n2', + } ) + .map( getBlockContent ) + .join( '' ); + + expect( transformed ).toBe( + '
1\n2' + ); + expect( console ).toHaveLogged(); + } ); + + it( 'should convert code', () => { + const transformed = pasteHandler( { + HTML: '
1\n2
',
+ plainText: '1\n2',
+ } )
+ .map( getBlockContent )
+ .join( '' );
+
+ expect( transformed ).toBe(
+ '1\n2
'
+ );
+ expect( console ).toHaveLogged();
+ } );
+
describe( 'pasteHandler', () => {
[
'plain',
diff --git a/test/integration/fixtures/blocks/core__social-link-gravatar.html b/test/integration/fixtures/blocks/core__social-link-gravatar.html
new file mode 100644
index 00000000000000..c4137b8a083176
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__social-link-gravatar.html
@@ -0,0 +1 @@
+
diff --git a/test/integration/fixtures/blocks/core__social-link-gravatar.json b/test/integration/fixtures/blocks/core__social-link-gravatar.json
new file mode 100644
index 00000000000000..2f4035d97640b2
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__social-link-gravatar.json
@@ -0,0 +1,11 @@
+[
+ {
+ "name": "core/social-link",
+ "isValid": true,
+ "attributes": {
+ "url": "https://example.com/",
+ "service": "gravatar"
+ },
+ "innerBlocks": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__social-link-gravatar.parsed.json b/test/integration/fixtures/blocks/core__social-link-gravatar.parsed.json
new file mode 100644
index 00000000000000..b4c7a8c146e142
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__social-link-gravatar.parsed.json
@@ -0,0 +1,11 @@
+[
+ {
+ "blockName": "core/social-link-gravatar",
+ "attrs": {
+ "url": "https://example.com/"
+ },
+ "innerBlocks": [],
+ "innerHTML": "",
+ "innerContent": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__social-link-gravatar.serialized.html b/test/integration/fixtures/blocks/core__social-link-gravatar.serialized.html
new file mode 100644
index 00000000000000..83a449d4e1f53c
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__social-link-gravatar.serialized.html
@@ -0,0 +1 @@
+
diff --git a/test/native/integration-test-helpers/add-block.js b/test/native/integration-test-helpers/add-block.js
index 5a15cb59fc6e16..eded603829c48a 100644
--- a/test/native/integration-test-helpers/add-block.js
+++ b/test/native/integration-test-helpers/add-block.js
@@ -6,7 +6,7 @@ import { Platform } from '@wordpress/element';
/**
* External dependencies
*/
-import { act, fireEvent } from '@testing-library/react-native';
+import { act, fireEvent, within } from '@testing-library/react-native';
import { AccessibilityInfo } from 'react-native';
/**
@@ -31,9 +31,9 @@ export const addBlock = async (
fireEvent.press( screen.getByLabelText( 'Add block' ) );
}
- const blockList = screen.getByTestId( 'InserterUI-Blocks' );
+ const inserterModal = screen.getByTestId( 'InserterUI-Blocks' );
// onScroll event used to force the FlatList to render all items
- fireEvent.scroll( blockList, {
+ fireEvent.scroll( inserterModal, {
nativeEvent: {
contentOffset: { y: 0, x: 0 },
contentSize: { width: 100, height: 100 },
@@ -41,7 +41,7 @@ export const addBlock = async (
},
} );
- const blockButton = await screen.findByText( blockName );
+ const blockButton = await within( inserterModal ).findByText( blockName );
// Blocks can perform belated state updates after they are inserted.
// To avoid potential `act` warnings, we ensure that all timers and queued
// microtasks are executed.
diff --git a/test/native/jest.config.js b/test/native/jest.config.js
index ad5c794ebbce88..4859ea597e0f63 100644
--- a/test/native/jest.config.js
+++ b/test/native/jest.config.js
@@ -24,7 +24,6 @@ const transpiledPackageNames = glob( 'packages/*/src/index.{js,ts}' ).map(
const RAW_HANDLING_UNSUPPORTED_UNIT_TESTS = [
'html-formatting-remover',
'phrasing-content-reducer',
- 'ms-list-converter',
'figure-content-reducer',
'special-comment-converter',
'normalise-blocks',
diff --git a/tools/webpack/interactivity.js b/tools/webpack/interactivity.js
index 26e49966ad40c3..03bb1f576cb783 100644
--- a/tools/webpack/interactivity.js
+++ b/tools/webpack/interactivity.js
@@ -2,28 +2,42 @@
* External dependencies
*/
const { join } = require( 'path' );
+const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
/**
* Internal dependencies
*/
-const { baseConfig } = require( './shared' );
+const { baseConfig, plugins } = require( './shared' );
module.exports = {
...baseConfig,
name: 'interactivity',
entry: {
- index: {
- import: `./packages/interactivity/src/index.js`,
- library: {
- name: [ 'wp', 'interactivity' ],
- type: 'window',
- },
- },
+ index: `./packages/interactivity/src/index.js`,
+ navigation: './packages/block-library/src/navigation/view.js',
+ query: './packages/block-library/src/query/view.js',
+ image: './packages/block-library/src/image/view.js',
+ file: './packages/block-library/src/file/view.js',
+ search: './packages/block-library/src/search/view.js',
+ },
+ experiments: {
+ outputModule: true,
},
output: {
devtoolNamespace: 'wp',
filename: './build/interactivity/[name].min.js',
+ library: {
+ type: 'module',
+ },
path: join( __dirname, '..', '..' ),
+ environment: { module: true },
+ },
+ externalsType: 'module',
+ externals: {
+ '@wordpress/interactivity': '@wordpress/interactivity',
+ },
+ resolve: {
+ extensions: [ '.js', '.ts', '.tsx' ],
},
module: {
rules: [
@@ -39,6 +53,7 @@ module.exports = {
babelrc: false,
configFile: false,
presets: [
+ '@babel/preset-typescript',
[
'@babel/preset-react',
{
@@ -53,6 +68,18 @@ module.exports = {
},
],
},
+ plugins: [
+ ...plugins,
+ // TODO: Move it to a different Webpack file.
+ new CopyWebpackPlugin( {
+ patterns: [
+ {
+ from: './node_modules/es-module-shims/dist/es-module-shims.wasm.js',
+ to: './build/modules/importmap-polyfill.min.js',
+ },
+ ],
+ } ),
+ ],
watchOptions: {
ignored: [ '**/node_modules' ],
aggregateTimeout: 500,
diff --git a/tsconfig.json b/tsconfig.json
index 4ee1787a247cf7..d05e883ed70b03 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -27,6 +27,7 @@
{ "path": "packages/html-entities" },
{ "path": "packages/i18n" },
{ "path": "packages/icons" },
+ { "path": "packages/interactivity" },
{ "path": "packages/is-shallow-equal" },
{ "path": "packages/keycodes" },
{ "path": "packages/lazy-import" },