@wordpress/block-editor 15.23.0 → 16.0.1-next.v.202607070741.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/build/autocompleters/block.cjs +10 -2
- package/build/autocompleters/block.cjs.map +2 -2
- package/build/components/background-image-control/index.cjs +0 -2
- package/build/components/background-image-control/index.cjs.map +2 -2
- package/build/components/block-inspector/edit-contents.cjs +3 -0
- package/build/components/block-inspector/edit-contents.cjs.map +3 -3
- package/build/components/block-inspector/index.cjs +2 -3
- package/build/components/block-inspector/index.cjs.map +2 -2
- package/build/components/block-list/block-invalid-warning.cjs +8 -19
- package/build/components/block-list/block-invalid-warning.cjs.map +2 -2
- package/build/components/block-styles/use-styles-for-block.cjs +2 -1
- package/build/components/block-styles/use-styles-for-block.cjs.map +2 -2
- package/build/components/block-title/use-block-display-title.cjs +7 -2
- package/build/components/block-title/use-block-display-title.cjs.map +2 -2
- package/build/components/block-toolbar/block-toolbar-icon.cjs +10 -14
- package/build/components/block-toolbar/block-toolbar-icon.cjs.map +2 -2
- package/build/components/block-variation-transforms/index.cjs +0 -1
- package/build/components/block-variation-transforms/index.cjs.map +2 -2
- package/build/components/child-layout-control/index.cjs +0 -2
- package/build/components/child-layout-control/index.cjs.map +2 -2
- package/build/components/colors-gradients/control.cjs +5 -5
- package/build/components/colors-gradients/control.cjs.map +2 -2
- package/build/components/date-format-picker/index.cjs +0 -1
- package/build/components/date-format-picker/index.cjs.map +2 -2
- package/build/components/dimensions-tool/aspect-ratio-tool.cjs +1 -2
- package/build/components/dimensions-tool/aspect-ratio-tool.cjs.map +2 -2
- package/build/components/dimensions-tool/scale-tool.cjs +0 -1
- package/build/components/dimensions-tool/scale-tool.cjs.map +2 -2
- package/build/components/dimensions-tool/width-height-tool.cjs +2 -4
- package/build/components/dimensions-tool/width-height-tool.cjs.map +2 -2
- package/build/components/font-appearance-control/index.cjs +6 -25
- package/build/components/font-appearance-control/index.cjs.map +3 -3
- package/build/components/font-family/index.cjs +6 -15
- package/build/components/font-family/index.cjs.map +3 -3
- package/build/components/font-sizes/font-size-picker.cjs +1 -2
- package/build/components/font-sizes/font-size-picker.cjs.map +2 -2
- package/build/components/global-styles/border-panel.cjs +0 -1
- package/build/components/global-styles/border-panel.cjs.map +2 -2
- package/build/components/global-styles/dimensions-panel.cjs +0 -3
- package/build/components/global-styles/dimensions-panel.cjs.map +2 -2
- package/build/components/global-styles/typography-panel.cjs +4 -10
- package/build/components/global-styles/typography-panel.cjs.map +2 -2
- package/build/components/height-control/index.cjs +0 -2
- package/build/components/height-control/index.cjs.map +2 -2
- package/build/components/html-element-control/index.cjs +0 -1
- package/build/components/html-element-control/index.cjs.map +2 -2
- package/build/components/image-editor/index.cjs +5 -0
- package/build/components/image-editor/index.cjs.map +3 -3
- package/build/components/image-editor/zoom-dropdown.cjs +0 -1
- package/build/components/image-editor/zoom-dropdown.cjs.map +2 -2
- package/build/components/image-size-control/index.cjs +1 -3
- package/build/components/image-size-control/index.cjs.map +2 -2
- package/build/components/inner-content/index.cjs +107 -0
- package/build/components/inner-content/index.cjs.map +7 -0
- package/build/components/inserter/hooks/use-block-types-state.cjs +10 -2
- package/build/components/inserter/hooks/use-block-types-state.cjs.map +2 -2
- package/build/components/inserter/media-tab/hooks.cjs +34 -6
- package/build/components/inserter/media-tab/hooks.cjs.map +2 -2
- package/build/components/inserter/media-tab/media-list.cjs +3 -1
- package/build/components/inserter/media-tab/media-list.cjs.map +2 -2
- package/build/components/inserter/media-tab/media-panel.cjs +224 -27
- package/build/components/inserter/media-tab/media-panel.cjs.map +3 -3
- package/build/components/inserter/media-tab/media-preview.cjs +32 -17
- package/build/components/inserter/media-tab/media-preview.cjs.map +2 -2
- package/build/components/inserter/media-tab/media-tab.cjs +26 -6
- package/build/components/inserter/media-tab/media-tab.cjs.map +2 -2
- package/build/components/inserter/mobile-tab-navigation.cjs +6 -1
- package/build/components/inserter/mobile-tab-navigation.cjs.map +2 -2
- package/build/components/inserter/no-results.cjs +2 -2
- package/build/components/inserter/no-results.cjs.map +2 -2
- package/build/components/inserter/preview-panel.cjs +2 -1
- package/build/components/inserter/preview-panel.cjs.map +2 -2
- package/build/components/inserter-list-item/index.cjs +8 -2
- package/build/components/inserter-list-item/index.cjs.map +2 -2
- package/build/components/letter-spacing-control/index.cjs +0 -24
- package/build/components/letter-spacing-control/index.cjs.map +3 -3
- package/build/components/line-height-control/index.cjs +7 -22
- package/build/components/line-height-control/index.cjs.map +3 -3
- package/build/components/list-view/block-select-button.cjs +90 -84
- package/build/components/list-view/block-select-button.cjs.map +2 -2
- package/build/components/list-view/block.cjs +51 -20
- package/build/components/list-view/block.cjs.map +2 -2
- package/build/components/list-view/expander.cjs +1 -1
- package/build/components/list-view/expander.cjs.map +2 -2
- package/build/components/list-view/use-list-view-client-ids.cjs +2 -2
- package/build/components/list-view/use-list-view-client-ids.cjs.map +2 -2
- package/build/components/observe-typing/index.cjs +4 -3
- package/build/components/observe-typing/index.cjs.map +2 -2
- package/build/components/preset-input-control/custom-value-controls.cjs +1 -3
- package/build/components/preset-input-control/custom-value-controls.cjs.map +2 -2
- package/build/components/preset-input-control/index.cjs +1 -3
- package/build/components/preset-input-control/index.cjs.map +2 -2
- package/build/components/resolution-tool/index.cjs +1 -2
- package/build/components/resolution-tool/index.cjs.map +2 -2
- package/build/components/text-alignment-control/index.cjs +0 -1
- package/build/components/text-alignment-control/index.cjs.map +2 -2
- package/build/components/text-decoration-control/index.cjs +0 -1
- package/build/components/text-decoration-control/index.cjs.map +2 -2
- package/build/components/text-indent-control/index.cjs +0 -8
- package/build/components/text-indent-control/index.cjs.map +2 -2
- package/build/components/text-transform-control/index.cjs +0 -1
- package/build/components/text-transform-control/index.cjs.map +2 -2
- package/build/components/use-block-display-information/index.cjs +10 -8
- package/build/components/use-block-display-information/index.cjs.map +2 -2
- package/build/components/use-on-block-drop/index.cjs +3 -7
- package/build/components/use-on-block-drop/index.cjs.map +2 -2
- package/build/components/writing-mode-control/index.cjs +0 -1
- package/build/components/writing-mode-control/index.cjs.map +2 -2
- package/build/hooks/font-size.cjs +1 -2
- package/build/hooks/font-size.cjs.map +2 -2
- package/build/hooks/layout.cjs +5 -3
- package/build/hooks/layout.cjs.map +2 -2
- package/build/hooks/position.cjs +36 -16
- package/build/hooks/position.cjs.map +2 -2
- package/build/hooks/states.cjs +6 -9
- package/build/hooks/states.cjs.map +2 -2
- package/build/layouts/constrained.cjs +21 -21
- package/build/layouts/constrained.cjs.map +2 -2
- package/build/layouts/flex.cjs +0 -3
- package/build/layouts/flex.cjs.map +2 -2
- package/build/layouts/grid.cjs +51 -9
- package/build/layouts/grid.cjs.map +2 -2
- package/build/private-apis.cjs +4 -1
- package/build/private-apis.cjs.map +3 -3
- package/build/store/actions.cjs.map +2 -2
- package/build/store/private-keys.cjs +3 -0
- package/build/store/private-keys.cjs.map +2 -2
- package/build/store/private-selectors.cjs +72 -4
- package/build/store/private-selectors.cjs.map +2 -2
- package/build/store/selectors.cjs +13 -0
- package/build/store/selectors.cjs.map +2 -2
- package/build-module/autocompleters/block.mjs +10 -2
- package/build-module/autocompleters/block.mjs.map +2 -2
- package/build-module/components/background-image-control/index.mjs +0 -2
- package/build-module/components/background-image-control/index.mjs.map +2 -2
- package/build-module/components/block-inspector/edit-contents.mjs +4 -1
- package/build-module/components/block-inspector/edit-contents.mjs.map +2 -2
- package/build-module/components/block-inspector/index.mjs +2 -3
- package/build-module/components/block-inspector/index.mjs.map +2 -2
- package/build-module/components/block-list/block-invalid-warning.mjs +8 -19
- package/build-module/components/block-list/block-invalid-warning.mjs.map +2 -2
- package/build-module/components/block-styles/use-styles-for-block.mjs +2 -1
- package/build-module/components/block-styles/use-styles-for-block.mjs.map +2 -2
- package/build-module/components/block-title/use-block-display-title.mjs +7 -2
- package/build-module/components/block-title/use-block-display-title.mjs.map +2 -2
- package/build-module/components/block-toolbar/block-toolbar-icon.mjs +10 -14
- package/build-module/components/block-toolbar/block-toolbar-icon.mjs.map +2 -2
- package/build-module/components/block-variation-transforms/index.mjs +0 -1
- package/build-module/components/block-variation-transforms/index.mjs.map +2 -2
- package/build-module/components/child-layout-control/index.mjs +0 -2
- package/build-module/components/child-layout-control/index.mjs.map +2 -2
- package/build-module/components/colors-gradients/control.mjs +5 -5
- package/build-module/components/colors-gradients/control.mjs.map +2 -2
- package/build-module/components/date-format-picker/index.mjs +0 -1
- package/build-module/components/date-format-picker/index.mjs.map +2 -2
- package/build-module/components/dimensions-tool/aspect-ratio-tool.mjs +1 -2
- package/build-module/components/dimensions-tool/aspect-ratio-tool.mjs.map +2 -2
- package/build-module/components/dimensions-tool/scale-tool.mjs +0 -1
- package/build-module/components/dimensions-tool/scale-tool.mjs.map +2 -2
- package/build-module/components/dimensions-tool/width-height-tool.mjs +2 -4
- package/build-module/components/dimensions-tool/width-height-tool.mjs.map +2 -2
- package/build-module/components/font-appearance-control/index.mjs +6 -15
- package/build-module/components/font-appearance-control/index.mjs.map +2 -2
- package/build-module/components/font-family/index.mjs +6 -15
- package/build-module/components/font-family/index.mjs.map +2 -2
- package/build-module/components/font-sizes/font-size-picker.mjs +1 -2
- package/build-module/components/font-sizes/font-size-picker.mjs.map +2 -2
- package/build-module/components/global-styles/border-panel.mjs +0 -1
- package/build-module/components/global-styles/border-panel.mjs.map +2 -2
- package/build-module/components/global-styles/dimensions-panel.mjs +0 -3
- package/build-module/components/global-styles/dimensions-panel.mjs.map +2 -2
- package/build-module/components/global-styles/typography-panel.mjs +4 -10
- package/build-module/components/global-styles/typography-panel.mjs.map +2 -2
- package/build-module/components/height-control/index.mjs +0 -2
- package/build-module/components/height-control/index.mjs.map +2 -2
- package/build-module/components/html-element-control/index.mjs +0 -1
- package/build-module/components/html-element-control/index.mjs.map +2 -2
- package/build-module/components/image-editor/index.mjs +5 -0
- package/build-module/components/image-editor/index.mjs.map +2 -2
- package/build-module/components/image-editor/zoom-dropdown.mjs +0 -1
- package/build-module/components/image-editor/zoom-dropdown.mjs.map +2 -2
- package/build-module/components/image-size-control/index.mjs +1 -3
- package/build-module/components/image-size-control/index.mjs.map +2 -2
- package/build-module/components/inner-content/index.mjs +82 -0
- package/build-module/components/inner-content/index.mjs.map +7 -0
- package/build-module/components/inserter/hooks/use-block-types-state.mjs +10 -2
- package/build-module/components/inserter/hooks/use-block-types-state.mjs.map +2 -2
- package/build-module/components/inserter/media-tab/hooks.mjs +33 -6
- package/build-module/components/inserter/media-tab/hooks.mjs.map +2 -2
- package/build-module/components/inserter/media-tab/media-list.mjs +3 -1
- package/build-module/components/inserter/media-tab/media-list.mjs.map +2 -2
- package/build-module/components/inserter/media-tab/media-panel.mjs +227 -30
- package/build-module/components/inserter/media-tab/media-panel.mjs.map +2 -2
- package/build-module/components/inserter/media-tab/media-preview.mjs +33 -18
- package/build-module/components/inserter/media-tab/media-preview.mjs.map +2 -2
- package/build-module/components/inserter/media-tab/media-tab.mjs +27 -7
- package/build-module/components/inserter/media-tab/media-tab.mjs.map +2 -2
- package/build-module/components/inserter/mobile-tab-navigation.mjs +6 -1
- package/build-module/components/inserter/mobile-tab-navigation.mjs.map +2 -2
- package/build-module/components/inserter/no-results.mjs +2 -2
- package/build-module/components/inserter/no-results.mjs.map +2 -2
- package/build-module/components/inserter/preview-panel.mjs +2 -1
- package/build-module/components/inserter/preview-panel.mjs.map +2 -2
- package/build-module/components/inserter-list-item/index.mjs +8 -2
- package/build-module/components/inserter-list-item/index.mjs.map +2 -2
- package/build-module/components/letter-spacing-control/index.mjs +0 -14
- package/build-module/components/letter-spacing-control/index.mjs.map +2 -2
- package/build-module/components/line-height-control/index.mjs +7 -12
- package/build-module/components/line-height-control/index.mjs.map +2 -2
- package/build-module/components/list-view/block-select-button.mjs +90 -84
- package/build-module/components/list-view/block-select-button.mjs.map +2 -2
- package/build-module/components/list-view/block.mjs +51 -20
- package/build-module/components/list-view/block.mjs.map +2 -2
- package/build-module/components/list-view/expander.mjs +1 -1
- package/build-module/components/list-view/expander.mjs.map +2 -2
- package/build-module/components/list-view/use-list-view-client-ids.mjs +2 -2
- package/build-module/components/list-view/use-list-view-client-ids.mjs.map +2 -2
- package/build-module/components/observe-typing/index.mjs +4 -3
- package/build-module/components/observe-typing/index.mjs.map +2 -2
- package/build-module/components/preset-input-control/custom-value-controls.mjs +1 -3
- package/build-module/components/preset-input-control/custom-value-controls.mjs.map +2 -2
- package/build-module/components/preset-input-control/index.mjs +1 -3
- package/build-module/components/preset-input-control/index.mjs.map +2 -2
- package/build-module/components/resolution-tool/index.mjs +1 -2
- package/build-module/components/resolution-tool/index.mjs.map +2 -2
- package/build-module/components/text-alignment-control/index.mjs +0 -1
- package/build-module/components/text-alignment-control/index.mjs.map +2 -2
- package/build-module/components/text-decoration-control/index.mjs +0 -1
- package/build-module/components/text-decoration-control/index.mjs.map +2 -2
- package/build-module/components/text-indent-control/index.mjs +0 -8
- package/build-module/components/text-indent-control/index.mjs.map +2 -2
- package/build-module/components/text-transform-control/index.mjs +0 -1
- package/build-module/components/text-transform-control/index.mjs.map +2 -2
- package/build-module/components/use-block-display-information/index.mjs +10 -8
- package/build-module/components/use-block-display-information/index.mjs.map +2 -2
- package/build-module/components/use-on-block-drop/index.mjs +4 -7
- package/build-module/components/use-on-block-drop/index.mjs.map +2 -2
- package/build-module/components/writing-mode-control/index.mjs +0 -1
- package/build-module/components/writing-mode-control/index.mjs.map +2 -2
- package/build-module/hooks/font-size.mjs +1 -2
- package/build-module/hooks/font-size.mjs.map +2 -2
- package/build-module/hooks/layout.mjs +4 -3
- package/build-module/hooks/layout.mjs.map +2 -2
- package/build-module/hooks/position.mjs +38 -18
- package/build-module/hooks/position.mjs.map +2 -2
- package/build-module/hooks/states.mjs +6 -9
- package/build-module/hooks/states.mjs.map +2 -2
- package/build-module/layouts/constrained.mjs +21 -21
- package/build-module/layouts/constrained.mjs.map +2 -2
- package/build-module/layouts/flex.mjs +0 -3
- package/build-module/layouts/flex.mjs.map +2 -2
- package/build-module/layouts/grid.mjs +52 -9
- package/build-module/layouts/grid.mjs.map +2 -2
- package/build-module/private-apis.mjs +5 -1
- package/build-module/private-apis.mjs.map +2 -2
- package/build-module/store/actions.mjs.map +2 -2
- package/build-module/store/private-keys.mjs +2 -0
- package/build-module/store/private-keys.mjs.map +2 -2
- package/build-module/store/private-selectors.mjs +71 -4
- package/build-module/store/private-selectors.mjs.map +2 -2
- package/build-module/store/selectors.mjs +13 -0
- package/build-module/store/selectors.mjs.map +2 -2
- package/build-style/content-rtl.css +11 -5
- package/build-style/content.css +11 -5
- package/build-style/style-rtl.css +113 -3
- package/build-style/style.css +113 -3
- package/package.json +43 -41
- package/src/autocompleters/block.js +10 -3
- package/src/autocompleters/test/block.js +53 -0
- package/src/components/background-image-control/index.js +0 -2
- package/src/components/block-inspector/edit-contents.js +5 -1
- package/src/components/block-inspector/index.js +3 -2
- package/src/components/block-list/block-invalid-warning.js +8 -19
- package/src/components/block-patterns-list/stories/index.story.jsx +1 -1
- package/src/components/block-styles/use-styles-for-block.js +1 -0
- package/src/components/block-title/use-block-display-title.js +7 -2
- package/src/components/block-toolbar/block-toolbar-icon.js +11 -16
- package/src/components/block-toolbar/test/block-toolbar-icon.js +32 -9
- package/src/components/block-variation-transforms/index.js +0 -1
- package/src/components/child-layout-control/index.js +0 -2
- package/src/components/color-palette/test/__snapshots__/control.js.snap +11 -106
- package/src/components/colors-gradients/control.js +4 -4
- package/src/components/colors-gradients/style.scss +2 -2
- package/src/components/date-format-picker/index.js +0 -1
- package/src/components/dimensions-tool/aspect-ratio-tool.js +0 -1
- package/src/components/dimensions-tool/scale-tool.js +0 -1
- package/src/components/dimensions-tool/width-height-tool.js +0 -2
- package/src/components/font-appearance-control/index.js +6 -19
- package/src/components/font-family/README.md +0 -9
- package/src/components/font-family/index.js +6 -19
- package/src/components/font-family/stories/index.story.jsx +0 -1
- package/src/components/font-sizes/font-size-picker.js +0 -1
- package/src/components/global-styles/border-panel.js +0 -1
- package/src/components/global-styles/dimensions-panel.js +0 -3
- package/src/components/global-styles/typography-panel.js +0 -6
- package/src/components/height-control/index.js +0 -2
- package/src/components/html-element-control/index.js +0 -1
- package/src/components/iframe/content.scss +3 -3
- package/src/components/image-editor/index.js +22 -0
- package/src/components/image-editor/zoom-dropdown.js +0 -1
- package/src/components/image-size-control/index.js +0 -2
- package/src/components/inner-content/index.js +119 -0
- package/src/components/inner-content/test/index.js +81 -0
- package/src/components/inserter/hooks/use-block-types-state.js +10 -2
- package/src/components/inserter/media-tab/hooks.js +59 -9
- package/src/components/inserter/media-tab/media-list.js +2 -0
- package/src/components/inserter/media-tab/media-panel.js +256 -17
- package/src/components/inserter/media-tab/media-preview.js +34 -17
- package/src/components/inserter/media-tab/media-tab.js +20 -2
- package/src/components/inserter/media-tab/test/hooks.js +204 -0
- package/src/components/inserter/mobile-tab-navigation.js +6 -1
- package/src/components/inserter/no-results.js +2 -2
- package/src/components/inserter/preview-panel.js +1 -0
- package/src/components/inserter/style.scss +110 -1
- package/src/components/inserter-list-item/index.js +8 -2
- package/src/components/letter-spacing-control/README.md +0 -8
- package/src/components/letter-spacing-control/index.js +5 -23
- package/src/components/letter-spacing-control/stories/index.story.jsx +0 -12
- package/src/components/line-height-control/README.md +0 -8
- package/src/components/line-height-control/index.js +7 -16
- package/src/components/line-height-control/stories/index.story.jsx +0 -1
- package/src/components/line-height-control/test/index.js +1 -7
- package/src/components/link-picker/test/index.js +3 -1
- package/src/components/list-view/block-select-button.js +5 -1
- package/src/components/list-view/block.js +57 -20
- package/src/components/list-view/expander.js +5 -1
- package/src/components/list-view/style.scss +19 -0
- package/src/components/list-view/use-list-view-client-ids.js +2 -2
- package/src/components/observe-typing/index.js +9 -4
- package/src/components/preset-input-control/custom-value-controls.js +0 -2
- package/src/components/preset-input-control/index.js +0 -2
- package/src/components/resolution-tool/index.js +0 -1
- package/src/components/responsive-block-control/test/index.js +1 -5
- package/src/components/text-alignment-control/index.js +0 -1
- package/src/components/text-decoration-control/index.js +0 -1
- package/src/components/text-indent-control/index.js +7 -16
- package/src/components/text-transform-control/index.js +0 -1
- package/src/components/use-block-display-information/index.js +9 -13
- package/src/components/use-block-display-information/test/index.js +47 -9
- package/src/components/use-on-block-drop/index.js +4 -7
- package/src/components/writing-mode-control/index.js +0 -1
- package/src/hooks/block-hooks.scss +15 -0
- package/src/hooks/font-size.js +0 -1
- package/src/hooks/layout.js +5 -4
- package/src/hooks/position.js +44 -23
- package/src/hooks/states.js +6 -12
- package/src/hooks/test/cross-origin-isolation.js +0 -4
- package/src/hooks/test/layout.js +75 -1
- package/src/layouts/constrained.js +51 -28
- package/src/layouts/flex.js +0 -3
- package/src/layouts/grid.js +59 -7
- package/src/layouts/test/constrained.js +91 -0
- package/src/layouts/test/grid.js +71 -0
- package/src/private-apis.js +4 -0
- package/src/store/actions.js +1 -0
- package/src/store/private-keys.js +1 -0
- package/src/store/private-selectors.js +141 -4
- package/src/store/selectors.js +41 -0
- package/src/store/test/private-selectors.js +422 -0
- package/src/store/test/reducer.js +11 -11
- package/src/store/test/selectors.js +133 -0
- package/src/style.scss +1 -0
|
@@ -1,29 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import clsx from 'clsx';
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* WordPress dependencies
|
|
3
8
|
*/
|
|
4
|
-
import { Spinner, SearchControl } from '@wordpress/components';
|
|
5
|
-
import { __ } from '@wordpress/i18n';
|
|
9
|
+
import { Button, Modal, Spinner, SearchControl } from '@wordpress/components';
|
|
10
|
+
import { __, _n, sprintf } from '@wordpress/i18n';
|
|
11
|
+
import { useCallback, useMemo, useState } from '@wordpress/element';
|
|
6
12
|
import { useDebouncedInput } from '@wordpress/compose';
|
|
13
|
+
import { useDispatch } from '@wordpress/data';
|
|
14
|
+
import { store as noticesStore } from '@wordpress/notices';
|
|
7
15
|
|
|
8
16
|
/**
|
|
9
17
|
* Internal dependencies
|
|
10
18
|
*/
|
|
11
19
|
import MediaList from './media-list';
|
|
12
|
-
import
|
|
20
|
+
import MediaUpload from '../../media-upload';
|
|
21
|
+
import MediaUploadCheck from '../../media-upload/check';
|
|
22
|
+
import { useMediaResults, useDelayedLoading } from './hooks';
|
|
13
23
|
import InserterNoResults from '../no-results';
|
|
14
24
|
|
|
15
25
|
const INITIAL_MEDIA_ITEMS_PER_PAGE = 10;
|
|
16
26
|
|
|
27
|
+
// The attach flow is image-only, so the picker is constrained to images.
|
|
28
|
+
const ATTACH_ALLOWED_TYPES = [ 'image' ];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Opens the Media Library to attach images to the current post. Only rendered
|
|
32
|
+
* for media categories that expose an `attach` capability (i.e. the "Attached
|
|
33
|
+
* images" source); other sources render the panel exactly as before.
|
|
34
|
+
*
|
|
35
|
+
* The picker opens fresh each time with no pre-selected value, so it is purely
|
|
36
|
+
* additive: selecting images attaches them, and it does not imply that
|
|
37
|
+
* deselecting would detach. Detaching is a separate, explicit per-item action.
|
|
38
|
+
*
|
|
39
|
+
* @param {Object} props
|
|
40
|
+
* @param {Function} props.onSelect Called with the selected media items.
|
|
41
|
+
*/
|
|
42
|
+
function AttachImagesButton( { onSelect } ) {
|
|
43
|
+
return (
|
|
44
|
+
<MediaUploadCheck>
|
|
45
|
+
<MediaUpload
|
|
46
|
+
multiple="add"
|
|
47
|
+
onSelect={ onSelect }
|
|
48
|
+
allowedTypes={ ATTACH_ALLOWED_TYPES }
|
|
49
|
+
title={ __( 'Attach images' ) }
|
|
50
|
+
render={ ( { open } ) => (
|
|
51
|
+
<Button
|
|
52
|
+
__next40pxDefaultSize
|
|
53
|
+
className="block-editor-inserter__media-panel-attach"
|
|
54
|
+
data-unstable-ignore-focus-outside-for-relatedtarget=".media-modal"
|
|
55
|
+
onClick={ ( event ) => {
|
|
56
|
+
event.target.focus();
|
|
57
|
+
open();
|
|
58
|
+
} }
|
|
59
|
+
variant="secondary"
|
|
60
|
+
>
|
|
61
|
+
{ __( 'Attach images' ) }
|
|
62
|
+
</Button>
|
|
63
|
+
) }
|
|
64
|
+
/>
|
|
65
|
+
</MediaUploadCheck>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
17
69
|
export function MediaCategoryPanel( { rootClientId, onInsert, category } ) {
|
|
18
70
|
const [ search, setSearch, debouncedSearch ] = useDebouncedInput();
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
71
|
+
const query = useMemo(
|
|
72
|
+
() => ( {
|
|
73
|
+
per_page: !! debouncedSearch ? 20 : INITIAL_MEDIA_ITEMS_PER_PAGE,
|
|
74
|
+
search: debouncedSearch,
|
|
75
|
+
} ),
|
|
76
|
+
[ debouncedSearch ]
|
|
77
|
+
);
|
|
78
|
+
const [ refreshKey, setRefreshKey ] = useState( 0 );
|
|
79
|
+
const { mediaList, isLoading } = useMediaResults(
|
|
80
|
+
category,
|
|
81
|
+
query,
|
|
82
|
+
refreshKey
|
|
83
|
+
);
|
|
84
|
+
const { createErrorNotice, createSuccessNotice, createWarningNotice } =
|
|
85
|
+
useDispatch( noticesStore );
|
|
86
|
+
// Dim (rather than blank) the populated grid while a refetch is in flight,
|
|
87
|
+
// but only once it has run long enough to be worth signalling — quick
|
|
88
|
+
// attach/detach refetches resolve before this and show nothing.
|
|
89
|
+
const showRefreshing = useDelayedLoading( isLoading );
|
|
90
|
+
|
|
91
|
+
// Invalidate the cached results and force `useMediaResults` to refetch so
|
|
92
|
+
// the grid reflects images that were just attached or detached.
|
|
93
|
+
const refresh = useCallback( () => {
|
|
94
|
+
category.invalidate?.( query );
|
|
95
|
+
setRefreshKey( ( key ) => key + 1 );
|
|
96
|
+
}, [ category, query ] );
|
|
97
|
+
|
|
98
|
+
const handleAttach = useCallback(
|
|
99
|
+
async ( selectedMedia ) => {
|
|
100
|
+
try {
|
|
101
|
+
const attachedCount = await category.attach( selectedMedia );
|
|
102
|
+
|
|
103
|
+
if ( ! attachedCount ) {
|
|
104
|
+
// This source only attaches images (the picker's "Upload
|
|
105
|
+
// files" tab otherwise accepts any file type), so a selection
|
|
106
|
+
// with no images attaches nothing.
|
|
107
|
+
createWarningNotice( __( 'No images were attached.' ), {
|
|
108
|
+
type: 'snackbar',
|
|
109
|
+
id: 'inserter-notice',
|
|
110
|
+
} );
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
refresh();
|
|
115
|
+
createSuccessNotice(
|
|
116
|
+
category.postTypeLabel
|
|
117
|
+
? sprintf(
|
|
118
|
+
/* translators: %1$d: Number of images attached. %2$s: Name of the post type e.g: "Page". */
|
|
119
|
+
_n(
|
|
120
|
+
'%1$d image attached to %2$s.',
|
|
121
|
+
'%1$d images attached to %2$s.',
|
|
122
|
+
attachedCount
|
|
123
|
+
),
|
|
124
|
+
attachedCount,
|
|
125
|
+
category.postTypeLabel
|
|
126
|
+
)
|
|
127
|
+
: sprintf(
|
|
128
|
+
/* translators: %d: Number of images attached to the post. */
|
|
129
|
+
_n(
|
|
130
|
+
'%d image attached to post.',
|
|
131
|
+
'%d images attached to post.',
|
|
132
|
+
attachedCount
|
|
133
|
+
),
|
|
134
|
+
attachedCount
|
|
135
|
+
),
|
|
136
|
+
{ type: 'snackbar', id: 'inserter-notice' }
|
|
137
|
+
);
|
|
138
|
+
} catch {
|
|
139
|
+
createErrorNotice( __( 'Could not attach images.' ), {
|
|
140
|
+
type: 'snackbar',
|
|
141
|
+
id: 'inserter-notice',
|
|
142
|
+
} );
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
[
|
|
146
|
+
category,
|
|
147
|
+
refresh,
|
|
148
|
+
createErrorNotice,
|
|
149
|
+
createSuccessNotice,
|
|
150
|
+
createWarningNotice,
|
|
151
|
+
]
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const handleDetach = useCallback(
|
|
155
|
+
async ( media ) => {
|
|
156
|
+
try {
|
|
157
|
+
await category.detach( media );
|
|
158
|
+
refresh();
|
|
159
|
+
createSuccessNotice(
|
|
160
|
+
category.postTypeLabel
|
|
161
|
+
? sprintf(
|
|
162
|
+
/* translators: %s: Name of the post type e.g: "Page". */
|
|
163
|
+
__( 'Image detached from %s.' ),
|
|
164
|
+
category.postTypeLabel
|
|
165
|
+
)
|
|
166
|
+
: __( 'Image detached from post.' ),
|
|
167
|
+
{ type: 'snackbar', id: 'inserter-notice' }
|
|
168
|
+
);
|
|
169
|
+
} catch {
|
|
170
|
+
createErrorNotice( __( 'Could not detach image.' ), {
|
|
171
|
+
type: 'snackbar',
|
|
172
|
+
id: 'inserter-notice',
|
|
173
|
+
} );
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
[ category, refresh, createErrorNotice, createSuccessNotice ]
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Detaching is confirmed first: the dropdown sets the pending item, which
|
|
180
|
+
// opens a modal, and only `confirmDetach` performs the detach.
|
|
181
|
+
const [ mediaPendingDetach, setMediaPendingDetach ] = useState();
|
|
182
|
+
const confirmDetach = useCallback( () => {
|
|
183
|
+
const media = mediaPendingDetach;
|
|
184
|
+
setMediaPendingDetach( undefined );
|
|
185
|
+
handleDetach( media );
|
|
186
|
+
}, [ handleDetach, mediaPendingDetach ] );
|
|
187
|
+
|
|
23
188
|
const baseCssClass = 'block-editor-inserter__media-panel';
|
|
24
189
|
const searchLabel = category.labels.search_items || __( 'Search' );
|
|
25
190
|
return (
|
|
26
|
-
<div
|
|
191
|
+
<div
|
|
192
|
+
className={ clsx( baseCssClass, {
|
|
193
|
+
// The attach footer supplies the breathing room beneath the
|
|
194
|
+
// grid, so the list drops its own bottom padding (see styles).
|
|
195
|
+
'has-attach-footer': !! category.attach,
|
|
196
|
+
} ) }
|
|
197
|
+
>
|
|
27
198
|
<SearchControl
|
|
28
199
|
className={ `${ baseCssClass }-search` }
|
|
29
200
|
onChange={ setSearch }
|
|
@@ -31,19 +202,87 @@ export function MediaCategoryPanel( { rootClientId, onInsert, category } ) {
|
|
|
31
202
|
label={ searchLabel }
|
|
32
203
|
placeholder={ searchLabel }
|
|
33
204
|
/>
|
|
34
|
-
{ isLoading && (
|
|
205
|
+
{ isLoading && ! mediaList?.length && (
|
|
35
206
|
<div className={ `${ baseCssClass }-spinner` }>
|
|
36
207
|
<Spinner />
|
|
37
208
|
</div>
|
|
38
209
|
) }
|
|
39
|
-
{ ! isLoading && ! mediaList?.length &&
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
210
|
+
{ ! isLoading && ! mediaList?.length && (
|
|
211
|
+
<InserterNoResults>
|
|
212
|
+
{ category.emptyMessage && ! debouncedSearch
|
|
213
|
+
? // For a source with a custom empty message (e.g.
|
|
214
|
+
// Attachments) and no active search, an empty result
|
|
215
|
+
// means nothing is attached yet — clearer than the
|
|
216
|
+
// generic "no results found".
|
|
217
|
+
category.emptyMessage
|
|
218
|
+
: __( 'No results found.' ) }
|
|
219
|
+
</InserterNoResults>
|
|
220
|
+
) }
|
|
221
|
+
{ !! mediaList?.length && (
|
|
222
|
+
// Keep the existing items visible while a refetch is in flight,
|
|
223
|
+
// dimming (and gently pulsing) them rather than clearing the grid,
|
|
224
|
+
// so it doesn't flicker or pop.
|
|
225
|
+
<div
|
|
226
|
+
className={ clsx( `${ baseCssClass }-results`, {
|
|
227
|
+
'is-loading': showRefreshing,
|
|
228
|
+
} ) }
|
|
229
|
+
aria-busy={ showRefreshing }
|
|
230
|
+
>
|
|
231
|
+
<MediaList
|
|
232
|
+
rootClientId={ rootClientId }
|
|
233
|
+
onClick={ onInsert }
|
|
234
|
+
onDetach={
|
|
235
|
+
category.detach ? setMediaPendingDetach : undefined
|
|
236
|
+
}
|
|
237
|
+
mediaList={ mediaList }
|
|
238
|
+
category={ category }
|
|
239
|
+
/>
|
|
240
|
+
</div>
|
|
241
|
+
) }
|
|
242
|
+
{ category.attach && (
|
|
243
|
+
// Pinned to the bottom of the panel as a fixed footer so it lines
|
|
244
|
+
// up with the "Open Media Library" button in the adjacent column.
|
|
245
|
+
<AttachImagesButton onSelect={ handleAttach } />
|
|
246
|
+
) }
|
|
247
|
+
{ mediaPendingDetach && (
|
|
248
|
+
// A plain `Modal` (not `ConfirmDialog`) so we can pass
|
|
249
|
+
// `overlayClassName` and stack it above the options dropdown that
|
|
250
|
+
// opened it (see the z-index entry in `_z-index.scss`).
|
|
251
|
+
<Modal
|
|
252
|
+
title={ __( 'Detach image' ) }
|
|
253
|
+
onRequestClose={ () => setMediaPendingDetach( undefined ) }
|
|
254
|
+
overlayClassName={ `${ baseCssClass }-detach-modal` }
|
|
255
|
+
>
|
|
256
|
+
<p>
|
|
257
|
+
{ category.postTypeLabel
|
|
258
|
+
? sprintf(
|
|
259
|
+
/* translators: %s: Name of the post type e.g: "Page". */
|
|
260
|
+
__(
|
|
261
|
+
'Detach this image from the current %s? The image will remain in the Media Library.'
|
|
262
|
+
),
|
|
263
|
+
category.postTypeLabel
|
|
264
|
+
)
|
|
265
|
+
: __(
|
|
266
|
+
'Detach this image from the current post? The image will remain in the Media Library.'
|
|
267
|
+
) }
|
|
268
|
+
</p>
|
|
269
|
+
<div className={ `${ baseCssClass }-detach-actions` }>
|
|
270
|
+
<Button
|
|
271
|
+
__next40pxDefaultSize
|
|
272
|
+
variant="tertiary"
|
|
273
|
+
onClick={ () => setMediaPendingDetach( undefined ) }
|
|
274
|
+
>
|
|
275
|
+
{ __( 'Cancel' ) }
|
|
276
|
+
</Button>
|
|
277
|
+
<Button
|
|
278
|
+
__next40pxDefaultSize
|
|
279
|
+
variant="primary"
|
|
280
|
+
onClick={ confirmDetach }
|
|
281
|
+
>
|
|
282
|
+
{ __( 'Detach' ) }
|
|
283
|
+
</Button>
|
|
284
|
+
</div>
|
|
285
|
+
</Modal>
|
|
47
286
|
) }
|
|
48
287
|
</div>
|
|
49
288
|
);
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
import { __, sprintf } from '@wordpress/i18n';
|
|
22
22
|
import { useMemo, useCallback, useState } from '@wordpress/element';
|
|
23
23
|
import { cloneBlock } from '@wordpress/blocks';
|
|
24
|
-
import { moreVertical, external } from '@wordpress/icons';
|
|
24
|
+
import { moreVertical, external, linkOff } from '@wordpress/icons';
|
|
25
25
|
import { useSelect, useDispatch } from '@wordpress/data';
|
|
26
26
|
import { store as noticesStore } from '@wordpress/notices';
|
|
27
27
|
import { isBlobURL } from '@wordpress/blob';
|
|
@@ -43,11 +43,11 @@ const MEDIA_OPTIONS_POPOVER_PROPS = {
|
|
|
43
43
|
'block-editor-inserter__media-list__item-preview-options__popover',
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
function MediaPreviewOptions( { category, media } ) {
|
|
47
|
-
if ( ! category.getReportUrl ) {
|
|
46
|
+
function MediaPreviewOptions( { category, media, onDetach } ) {
|
|
47
|
+
if ( ! category.getReportUrl && ! onDetach ) {
|
|
48
48
|
return null;
|
|
49
49
|
}
|
|
50
|
-
const reportUrl = category.getReportUrl( media );
|
|
50
|
+
const reportUrl = category.getReportUrl?.( media );
|
|
51
51
|
return (
|
|
52
52
|
<DropdownMenu
|
|
53
53
|
className="block-editor-inserter__media-list__item-preview-options"
|
|
@@ -57,18 +57,34 @@ function MediaPreviewOptions( { category, media } ) {
|
|
|
57
57
|
>
|
|
58
58
|
{ () => (
|
|
59
59
|
<MenuGroup>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
{ reportUrl && (
|
|
61
|
+
<MenuItem
|
|
62
|
+
onClick={ () =>
|
|
63
|
+
window.open( reportUrl, '_blank' ).focus()
|
|
64
|
+
}
|
|
65
|
+
icon={ external }
|
|
66
|
+
>
|
|
67
|
+
{ sprintf(
|
|
68
|
+
/* translators: %s: The media type to report e.g: "image", "video", "audio" */
|
|
69
|
+
__( 'Report %s' ),
|
|
70
|
+
category.mediaType
|
|
71
|
+
) }
|
|
72
|
+
</MenuItem>
|
|
73
|
+
) }
|
|
74
|
+
{ onDetach && (
|
|
75
|
+
<MenuItem
|
|
76
|
+
onClick={ () => onDetach( media ) }
|
|
77
|
+
icon={ linkOff }
|
|
78
|
+
>
|
|
79
|
+
{ category.postTypeLabel
|
|
80
|
+
? sprintf(
|
|
81
|
+
/* translators: %s: Name of the post type e.g: "Page". */
|
|
82
|
+
__( 'Detach from %s' ),
|
|
83
|
+
category.postTypeLabel
|
|
84
|
+
)
|
|
85
|
+
: __( 'Detach from post' ) }
|
|
86
|
+
</MenuItem>
|
|
87
|
+
) }
|
|
72
88
|
</MenuGroup>
|
|
73
89
|
) }
|
|
74
90
|
</DropdownMenu>
|
|
@@ -122,7 +138,7 @@ function InsertExternalImageModal( { onClose, onSubmit } ) {
|
|
|
122
138
|
);
|
|
123
139
|
}
|
|
124
140
|
|
|
125
|
-
export function MediaPreview( { media, onClick, category } ) {
|
|
141
|
+
export function MediaPreview( { media, onClick, onDetach, category } ) {
|
|
126
142
|
const [ showExternalUploadModal, setShowExternalUploadModal ] =
|
|
127
143
|
useState( false );
|
|
128
144
|
const [ isHovered, setIsHovered ] = useState( false );
|
|
@@ -294,6 +310,7 @@ export function MediaPreview( { media, onClick, category } ) {
|
|
|
294
310
|
<MediaPreviewOptions
|
|
295
311
|
category={ category }
|
|
296
312
|
media={ media }
|
|
313
|
+
onDetach={ onDetach }
|
|
297
314
|
/>
|
|
298
315
|
) }
|
|
299
316
|
</div>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { __ } from '@wordpress/i18n';
|
|
5
5
|
import { useViewportMatch } from '@wordpress/compose';
|
|
6
6
|
import { Button } from '@wordpress/components';
|
|
7
|
-
import { useCallback, useMemo } from '@wordpress/element';
|
|
7
|
+
import { useCallback, useEffect, useMemo } from '@wordpress/element';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Internal dependencies
|
|
@@ -54,6 +54,21 @@ function MediaTab( {
|
|
|
54
54
|
} ) ),
|
|
55
55
|
[ mediaCategories ]
|
|
56
56
|
);
|
|
57
|
+
useEffect( () => {
|
|
58
|
+
if ( ! selectedCategory ) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const latestCategory = categories.find(
|
|
63
|
+
( category ) => category.name === selectedCategory.name
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
if ( latestCategory && latestCategory !== selectedCategory ) {
|
|
67
|
+
onSelectCategory( latestCategory );
|
|
68
|
+
} else if ( ! latestCategory ) {
|
|
69
|
+
onSelectCategory( null );
|
|
70
|
+
}
|
|
71
|
+
}, [ categories, onSelectCategory, selectedCategory ] );
|
|
57
72
|
|
|
58
73
|
if ( ! categories.length ) {
|
|
59
74
|
return <InserterNoResults />;
|
|
@@ -100,7 +115,10 @@ function MediaTab( {
|
|
|
100
115
|
</div>
|
|
101
116
|
) }
|
|
102
117
|
{ isMobile && (
|
|
103
|
-
<MobileTabNavigation
|
|
118
|
+
<MobileTabNavigation
|
|
119
|
+
categories={ categories }
|
|
120
|
+
screenClassName="block-editor-inserter__media-mobile-screen"
|
|
121
|
+
>
|
|
104
122
|
{ ( category ) => (
|
|
105
123
|
<MediaCategoryPanel
|
|
106
124
|
onInsert={ onInsert }
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { renderHook, act, waitFor } from '@testing-library/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { useDelayedLoading, useMediaResults } from '../hooks';
|
|
10
|
+
|
|
11
|
+
describe( 'useDelayedLoading', () => {
|
|
12
|
+
beforeEach( () => {
|
|
13
|
+
jest.useFakeTimers();
|
|
14
|
+
} );
|
|
15
|
+
afterEach( () => {
|
|
16
|
+
jest.useRealTimers();
|
|
17
|
+
} );
|
|
18
|
+
|
|
19
|
+
it( 'does not surface loading before the delay elapses', () => {
|
|
20
|
+
const { result } = renderHook( () => useDelayedLoading( true, 400 ) );
|
|
21
|
+
expect( result.current ).toBe( false );
|
|
22
|
+
act( () => {
|
|
23
|
+
jest.advanceTimersByTime( 399 );
|
|
24
|
+
} );
|
|
25
|
+
expect( result.current ).toBe( false );
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
it( 'surfaces loading once the delay elapses', () => {
|
|
29
|
+
const { result } = renderHook( () => useDelayedLoading( true, 400 ) );
|
|
30
|
+
act( () => {
|
|
31
|
+
jest.advanceTimersByTime( 400 );
|
|
32
|
+
} );
|
|
33
|
+
expect( result.current ).toBe( true );
|
|
34
|
+
} );
|
|
35
|
+
|
|
36
|
+
it( 'never surfaces loading for an operation that ends before the delay', () => {
|
|
37
|
+
const { result, rerender } = renderHook(
|
|
38
|
+
( { isLoading } ) => useDelayedLoading( isLoading, 400 ),
|
|
39
|
+
{ initialProps: { isLoading: true } }
|
|
40
|
+
);
|
|
41
|
+
act( () => {
|
|
42
|
+
jest.advanceTimersByTime( 200 );
|
|
43
|
+
} );
|
|
44
|
+
rerender( { isLoading: false } );
|
|
45
|
+
act( () => {
|
|
46
|
+
jest.advanceTimersByTime( 400 );
|
|
47
|
+
} );
|
|
48
|
+
expect( result.current ).toBe( false );
|
|
49
|
+
} );
|
|
50
|
+
|
|
51
|
+
it( 'resets once loading finishes', () => {
|
|
52
|
+
const { result, rerender } = renderHook(
|
|
53
|
+
( { isLoading } ) => useDelayedLoading( isLoading, 400 ),
|
|
54
|
+
{ initialProps: { isLoading: true } }
|
|
55
|
+
);
|
|
56
|
+
act( () => {
|
|
57
|
+
jest.advanceTimersByTime( 400 );
|
|
58
|
+
} );
|
|
59
|
+
expect( result.current ).toBe( true );
|
|
60
|
+
rerender( { isLoading: false } );
|
|
61
|
+
expect( result.current ).toBe( false );
|
|
62
|
+
} );
|
|
63
|
+
} );
|
|
64
|
+
|
|
65
|
+
describe( 'useMediaResults', () => {
|
|
66
|
+
const createCategory = ( name, items ) => ( {
|
|
67
|
+
name,
|
|
68
|
+
fetch: jest.fn( async () => items ),
|
|
69
|
+
} );
|
|
70
|
+
|
|
71
|
+
it( 'fetches and returns media for the query', async () => {
|
|
72
|
+
const category = createCategory( 'images', [ { id: 1 } ] );
|
|
73
|
+
const { result } = renderHook( () =>
|
|
74
|
+
useMediaResults( category, { search: '' }, 0 )
|
|
75
|
+
);
|
|
76
|
+
await waitFor( () => expect( result.current.isLoading ).toBe( false ) );
|
|
77
|
+
expect( category.fetch ).toHaveBeenCalledWith( { search: '' } );
|
|
78
|
+
expect( result.current.mediaList ).toEqual( [ { id: 1 } ] );
|
|
79
|
+
} );
|
|
80
|
+
|
|
81
|
+
it( 'clears the previous results when the query changes', async () => {
|
|
82
|
+
const category = createCategory( 'images', [ { id: 1 } ] );
|
|
83
|
+
const { result, rerender } = renderHook(
|
|
84
|
+
( { query } ) => useMediaResults( category, query, 0 ),
|
|
85
|
+
{ initialProps: { query: { search: 'a' } } }
|
|
86
|
+
);
|
|
87
|
+
await waitFor( () =>
|
|
88
|
+
expect( result.current.mediaList ).toEqual( [ { id: 1 } ] )
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Hold the next request open so the intermediate (cleared) state is observable.
|
|
92
|
+
let resolveFetch;
|
|
93
|
+
category.fetch.mockImplementationOnce(
|
|
94
|
+
() =>
|
|
95
|
+
new Promise( ( resolve ) => {
|
|
96
|
+
resolveFetch = resolve;
|
|
97
|
+
} )
|
|
98
|
+
);
|
|
99
|
+
rerender( { query: { search: 'b' } } );
|
|
100
|
+
|
|
101
|
+
// A query change blanks the grid while the new request is in flight.
|
|
102
|
+
await waitFor( () => expect( result.current.mediaList ).toEqual( [] ) );
|
|
103
|
+
await act( async () => {
|
|
104
|
+
resolveFetch( [ { id: 2 } ] );
|
|
105
|
+
} );
|
|
106
|
+
expect( result.current.mediaList ).toEqual( [ { id: 2 } ] );
|
|
107
|
+
} );
|
|
108
|
+
|
|
109
|
+
it( 'keeps the existing results while a refresh (refreshKey bump) refetches', async () => {
|
|
110
|
+
const category = createCategory( 'attached-images', [ { id: 1 } ] );
|
|
111
|
+
const { result, rerender } = renderHook(
|
|
112
|
+
( { refreshKey } ) =>
|
|
113
|
+
useMediaResults( category, { search: '' }, refreshKey ),
|
|
114
|
+
{ initialProps: { refreshKey: 0 } }
|
|
115
|
+
);
|
|
116
|
+
await waitFor( () =>
|
|
117
|
+
expect( result.current.mediaList ).toEqual( [ { id: 1 } ] )
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Hold the refetch open so we can assert the grid is not blanked.
|
|
121
|
+
let resolveFetch;
|
|
122
|
+
category.fetch.mockImplementationOnce(
|
|
123
|
+
() =>
|
|
124
|
+
new Promise( ( resolve ) => {
|
|
125
|
+
resolveFetch = resolve;
|
|
126
|
+
} )
|
|
127
|
+
);
|
|
128
|
+
rerender( { refreshKey: 1 } );
|
|
129
|
+
|
|
130
|
+
// Same query, only `refreshKey` changed: existing items stay put.
|
|
131
|
+
await waitFor( () => expect( result.current.isLoading ).toBe( true ) );
|
|
132
|
+
expect( result.current.mediaList ).toEqual( [ { id: 1 } ] );
|
|
133
|
+
await act( async () => {
|
|
134
|
+
resolveFetch( [ { id: 2 } ] );
|
|
135
|
+
} );
|
|
136
|
+
expect( result.current.mediaList ).toEqual( [ { id: 2 } ] );
|
|
137
|
+
} );
|
|
138
|
+
|
|
139
|
+
it( 'clears and refetches when the category source changes with the same name and query', async () => {
|
|
140
|
+
const firstCategory = createCategory( 'attached-images', [
|
|
141
|
+
{ id: 1 },
|
|
142
|
+
] );
|
|
143
|
+
const { result, rerender } = renderHook(
|
|
144
|
+
( { category } ) => useMediaResults( category, { search: '' }, 0 ),
|
|
145
|
+
{ initialProps: { category: firstCategory } }
|
|
146
|
+
);
|
|
147
|
+
await waitFor( () =>
|
|
148
|
+
expect( result.current.mediaList ).toEqual( [ { id: 1 } ] )
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
let resolveFetch;
|
|
152
|
+
const secondCategory = {
|
|
153
|
+
name: 'attached-images',
|
|
154
|
+
fetch: jest.fn(
|
|
155
|
+
() =>
|
|
156
|
+
new Promise( ( resolve ) => {
|
|
157
|
+
resolveFetch = resolve;
|
|
158
|
+
} )
|
|
159
|
+
),
|
|
160
|
+
};
|
|
161
|
+
rerender( { category: secondCategory } );
|
|
162
|
+
|
|
163
|
+
await waitFor( () =>
|
|
164
|
+
expect( secondCategory.fetch ).toHaveBeenCalledWith( {
|
|
165
|
+
search: '',
|
|
166
|
+
} )
|
|
167
|
+
);
|
|
168
|
+
expect( result.current.mediaList ).toEqual( [] );
|
|
169
|
+
expect( result.current.isLoading ).toBe( true );
|
|
170
|
+
|
|
171
|
+
await act( async () => {
|
|
172
|
+
resolveFetch( [ { id: 2 } ] );
|
|
173
|
+
} );
|
|
174
|
+
expect( result.current.mediaList ).toEqual( [ { id: 2 } ] );
|
|
175
|
+
} );
|
|
176
|
+
|
|
177
|
+
it( 'does not refetch when only the category wrapper changes', async () => {
|
|
178
|
+
const fetch = jest.fn( async () => [ { id: 1 } ] );
|
|
179
|
+
const { result, rerender } = renderHook(
|
|
180
|
+
( { category } ) => useMediaResults( category, { search: '' }, 0 ),
|
|
181
|
+
{
|
|
182
|
+
initialProps: {
|
|
183
|
+
category: {
|
|
184
|
+
name: 'images',
|
|
185
|
+
fetch,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
await waitFor( () =>
|
|
191
|
+
expect( result.current.mediaList ).toEqual( [ { id: 1 } ] )
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
rerender( {
|
|
195
|
+
category: {
|
|
196
|
+
name: 'images',
|
|
197
|
+
fetch,
|
|
198
|
+
},
|
|
199
|
+
} );
|
|
200
|
+
|
|
201
|
+
expect( fetch ).toHaveBeenCalledTimes( 1 );
|
|
202
|
+
expect( result.current.mediaList ).toEqual( [ { id: 1 } ] );
|
|
203
|
+
} );
|
|
204
|
+
} );
|
|
@@ -41,7 +41,11 @@ function ScreenHeader( { title } ) {
|
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
export default function MobileTabNavigation( {
|
|
44
|
+
export default function MobileTabNavigation( {
|
|
45
|
+
categories,
|
|
46
|
+
children,
|
|
47
|
+
screenClassName,
|
|
48
|
+
} ) {
|
|
45
49
|
return (
|
|
46
50
|
<Navigator
|
|
47
51
|
initialPath="/"
|
|
@@ -71,6 +75,7 @@ export default function MobileTabNavigation( { categories, children } ) {
|
|
|
71
75
|
{ categories.map( ( category ) => (
|
|
72
76
|
<Navigator.Screen
|
|
73
77
|
key={ category.name }
|
|
78
|
+
className={ screenClassName }
|
|
74
79
|
path={ `/category/${ category.name }` }
|
|
75
80
|
>
|
|
76
81
|
<ScreenHeader title={ __( 'Back' ) } />
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { __ } from '@wordpress/i18n';
|
|
5
5
|
|
|
6
|
-
function InserterNoResults() {
|
|
6
|
+
function InserterNoResults( { children = __( 'No results found.' ) } ) {
|
|
7
7
|
return (
|
|
8
8
|
<div className="block-editor-inserter__no-results">
|
|
9
|
-
<p>{
|
|
9
|
+
<p>{ children }</p>
|
|
10
10
|
</div>
|
|
11
11
|
);
|
|
12
12
|
}
|