@wordpress/components 32.2.1-next.v.202602191100.0 → 32.2.1
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 +35 -0
- package/build/alignment-matrix-control/cell.cjs +2 -2
- package/build/alignment-matrix-control/cell.cjs.map +1 -1
- package/build/alignment-matrix-control/index.cjs +2 -2
- package/build/alignment-matrix-control/index.cjs.map +1 -1
- package/build/angle-picker-control/angle-circle.cjs +2 -2
- package/build/angle-picker-control/angle-circle.cjs.map +1 -1
- package/build/autocomplete/index.cjs +15 -7
- package/build/autocomplete/index.cjs.map +2 -2
- package/build/date-time/time/index.cjs +1 -1
- package/build/date-time/time/index.cjs.map +2 -2
- package/build/date-time/utils.cjs +9 -0
- package/build/date-time/utils.cjs.map +2 -2
- package/build/private-apis.cjs +0 -4
- package/build/private-apis.cjs.map +3 -3
- package/build/sandbox/index.cjs +125 -1
- package/build/sandbox/index.cjs.map +2 -2
- package/build/select-control/styles/select-control-styles.cjs +8 -8
- package/build/select-control/styles/select-control-styles.cjs.map +2 -2
- package/build/snackbar/list.cjs +4 -1
- package/build/snackbar/list.cjs.map +2 -2
- package/build/toggle-group-control/toggle-group-control-option-base/styles.cjs +8 -8
- package/build/toggle-group-control/toggle-group-control-option-base/styles.cjs.map +2 -2
- package/build/validated-form-controls/control-with-error.cjs +26 -3
- package/build/validated-form-controls/control-with-error.cjs.map +2 -2
- package/build/validated-form-controls/validity-indicator.cjs +2 -0
- package/build/validated-form-controls/validity-indicator.cjs.map +2 -2
- package/build-module/alignment-matrix-control/cell.mjs +2 -2
- package/build-module/alignment-matrix-control/cell.mjs.map +1 -1
- package/build-module/alignment-matrix-control/index.mjs +2 -2
- package/build-module/alignment-matrix-control/index.mjs.map +1 -1
- package/build-module/angle-picker-control/angle-circle.mjs +2 -2
- package/build-module/angle-picker-control/angle-circle.mjs.map +1 -1
- package/build-module/autocomplete/index.mjs +13 -6
- package/build-module/autocomplete/index.mjs.map +2 -2
- package/build-module/date-time/time/index.mjs +2 -2
- package/build-module/date-time/time/index.mjs.map +2 -2
- package/build-module/date-time/utils.mjs +8 -0
- package/build-module/date-time/utils.mjs.map +2 -2
- package/build-module/private-apis.mjs +0 -4
- package/build-module/private-apis.mjs.map +2 -2
- package/build-module/sandbox/index.mjs +126 -2
- package/build-module/sandbox/index.mjs.map +2 -2
- package/build-module/select-control/styles/select-control-styles.mjs +8 -8
- package/build-module/select-control/styles/select-control-styles.mjs.map +2 -2
- package/build-module/snackbar/list.mjs +4 -1
- package/build-module/snackbar/list.mjs.map +2 -2
- package/build-module/toggle-group-control/toggle-group-control-option-base/styles.mjs +8 -8
- package/build-module/toggle-group-control/toggle-group-control-option-base/styles.mjs.map +2 -2
- package/build-module/validated-form-controls/control-with-error.mjs +27 -4
- package/build-module/validated-form-controls/control-with-error.mjs.map +2 -2
- package/build-module/validated-form-controls/validity-indicator.mjs +2 -0
- package/build-module/validated-form-controls/validity-indicator.mjs.map +2 -2
- package/build-style/style-rtl.css +7 -183
- package/build-style/style.css +7 -183
- package/build-types/alignment-matrix-control/styles/alignment-matrix-control-icon-styles.d.ts +19 -0
- package/build-types/alignment-matrix-control/styles/alignment-matrix-control-icon-styles.d.ts.map +1 -0
- package/build-types/alignment-matrix-control/styles/alignment-matrix-control-styles.d.ts +23 -0
- package/build-types/alignment-matrix-control/styles/alignment-matrix-control-styles.d.ts.map +1 -0
- package/build-types/angle-picker-control/styles/angle-picker-control-styles.d.ts +25 -0
- package/build-types/angle-picker-control/styles/angle-picker-control-styles.d.ts.map +1 -0
- package/build-types/autocomplete/get-autocomplete-match.d.ts +20 -0
- package/build-types/autocomplete/get-autocomplete-match.d.ts.map +1 -0
- package/build-types/autocomplete/index.d.ts +8 -0
- package/build-types/autocomplete/index.d.ts.map +1 -1
- package/build-types/autocomplete/test/get-autocomplete-match.d.ts +2 -0
- package/build-types/autocomplete/test/get-autocomplete-match.d.ts.map +1 -0
- package/build-types/badge/stories/e2e/index.story.d.ts +7 -0
- package/build-types/badge/stories/e2e/index.story.d.ts.map +1 -0
- package/build-types/border-control/border-control-style-picker/hook.d.ts +267 -0
- package/build-types/border-control/border-control-style-picker/hook.d.ts.map +1 -0
- package/build-types/box-control/all-input-control.d.ts +4 -0
- package/build-types/box-control/all-input-control.d.ts.map +1 -0
- package/build-types/box-control/axial-input-controls.d.ts +4 -0
- package/build-types/box-control/axial-input-controls.d.ts.map +1 -0
- package/build-types/box-control/input-controls.d.ts +4 -0
- package/build-types/box-control/input-controls.d.ts.map +1 -0
- package/build-types/box-control/styles/box-control-visualizer-styles.d.ts +46 -0
- package/build-types/box-control/styles/box-control-visualizer-styles.d.ts.map +1 -0
- package/build-types/box-control/unit-control.d.ts +4 -0
- package/build-types/box-control/unit-control.d.ts.map +1 -0
- package/build-types/custom-gradient-picker/test/index.d.ts +2 -0
- package/build-types/custom-gradient-picker/test/index.d.ts.map +1 -0
- package/build-types/custom-select-control/styles.d.ts +11 -0
- package/build-types/custom-select-control/styles.d.ts.map +1 -0
- package/build-types/date-time/date-picker/index.d.ts +23 -0
- package/build-types/date-time/date-picker/index.d.ts.map +1 -0
- package/build-types/date-time/date-picker/styles.d.ts +45 -0
- package/build-types/date-time/date-picker/styles.d.ts.map +1 -0
- package/build-types/date-time/date-picker/test/index.d.ts +2 -0
- package/build-types/date-time/date-picker/test/index.d.ts.map +1 -0
- package/build-types/date-time/date-picker/test/use-lilius.d.ts +2 -0
- package/build-types/date-time/date-picker/test/use-lilius.d.ts.map +1 -0
- package/build-types/date-time/date-picker/use-lilius/index.d.ts +169 -0
- package/build-types/date-time/date-picker/use-lilius/index.d.ts.map +1 -0
- package/build-types/date-time/time/index.d.ts.map +1 -1
- package/build-types/date-time/time-picker/index.d.ts +27 -0
- package/build-types/date-time/time-picker/index.d.ts.map +1 -0
- package/build-types/date-time/time-picker/styles.d.ts +93 -0
- package/build-types/date-time/time-picker/styles.d.ts.map +1 -0
- package/build-types/date-time/time-picker/test/index.d.ts +2 -0
- package/build-types/date-time/time-picker/test/index.d.ts.map +1 -0
- package/build-types/date-time/time-picker/time-input/index.d.ts +4 -0
- package/build-types/date-time/time-picker/time-input/index.d.ts.map +1 -0
- package/build-types/date-time/time-picker/time-input/test/index.d.ts +2 -0
- package/build-types/date-time/time-picker/time-input/test/index.d.ts.map +1 -0
- package/build-types/date-time/time-picker/timezone.d.ts +7 -0
- package/build-types/date-time/time-picker/timezone.d.ts.map +1 -0
- package/build-types/date-time/utils.d.ts +9 -0
- package/build-types/date-time/utils.d.ts.map +1 -1
- package/build-types/dimension-control/index.d.ts +28 -0
- package/build-types/dimension-control/index.d.ts.map +1 -0
- package/build-types/dimension-control/sizes.d.ts +26 -0
- package/build-types/dimension-control/sizes.d.ts.map +1 -0
- package/build-types/dimension-control/stories/index.story.d.ts +4 -0
- package/build-types/dimension-control/stories/index.story.d.ts.map +1 -0
- package/build-types/dimension-control/types.d.ts +47 -0
- package/build-types/dimension-control/types.d.ts.map +1 -0
- package/build-types/dropdown-menu-v2/index.d.ts +17 -0
- package/build-types/dropdown-menu-v2/index.d.ts.map +1 -0
- package/build-types/dropdown-menu-v2/stories/index.story.d.ts +11 -0
- package/build-types/dropdown-menu-v2/stories/index.story.d.ts.map +1 -0
- package/build-types/dropdown-menu-v2/styles.d.ts +42 -0
- package/build-types/dropdown-menu-v2/styles.d.ts.map +1 -0
- package/build-types/dropdown-menu-v2/test/index.d.ts +2 -0
- package/build-types/dropdown-menu-v2/test/index.d.ts.map +1 -0
- package/build-types/dropdown-menu-v2/types.d.ts +252 -0
- package/build-types/dropdown-menu-v2/types.d.ts.map +1 -0
- package/build-types/external-link/styles/external-link-styles.d.ts +10 -0
- package/build-types/external-link/styles/external-link-styles.d.ts.map +1 -0
- package/build-types/mobile/inserter-button/sparkles.d.ts +3 -0
- package/build-types/mobile/inserter-button/sparkles.d.ts.map +1 -0
- package/build-types/navigator/navigator-back-button/index.d.ts +2 -0
- package/build-types/navigator/navigator-back-button/index.d.ts.map +1 -0
- package/build-types/navigator/navigator-button/index.d.ts +2 -0
- package/build-types/navigator/navigator-button/index.d.ts.map +1 -0
- package/build-types/navigator/navigator-provider/component.d.ts +38 -0
- package/build-types/navigator/navigator-provider/component.d.ts.map +1 -0
- package/build-types/navigator/navigator-provider/index.d.ts +2 -0
- package/build-types/navigator/navigator-provider/index.d.ts.map +1 -0
- package/build-types/navigator/navigator-screen/index.d.ts +2 -0
- package/build-types/navigator/navigator-screen/index.d.ts.map +1 -0
- package/build-types/navigator/navigator-to-parent-button/index.d.ts +2 -0
- package/build-types/navigator/navigator-to-parent-button/index.d.ts.map +1 -0
- package/build-types/private-apis.d.ts.map +1 -1
- package/build-types/radio-group/radio/index.d.ts +7 -0
- package/build-types/radio-group/radio/index.d.ts.map +1 -0
- package/build-types/radio-group/radio-context/index.d.ts +6 -0
- package/build-types/radio-group/radio-context/index.d.ts.map +1 -0
- package/build-types/sandbox/index.d.ts +1 -1
- package/build-types/sandbox/index.d.ts.map +1 -1
- package/build-types/sandbox/types.d.ts +11 -0
- package/build-types/sandbox/types.d.ts.map +1 -1
- package/build-types/select-control/styles/select-control-styles.d.ts.map +1 -1
- package/build-types/slot-fill/bubbles-virtually/fill.d.ts +5 -0
- package/build-types/slot-fill/bubbles-virtually/fill.d.ts.map +1 -0
- package/build-types/slot-fill/bubbles-virtually/slot-fill-context.d.ts +12 -0
- package/build-types/slot-fill/bubbles-virtually/slot-fill-context.d.ts.map +1 -0
- package/build-types/slot-fill/bubbles-virtually/slot-fill-provider.d.ts +4 -0
- package/build-types/slot-fill/bubbles-virtually/slot-fill-provider.d.ts.map +1 -0
- package/build-types/slot-fill/use-slot.d.ts +9 -0
- package/build-types/slot-fill/use-slot.d.ts.map +1 -0
- package/build-types/snackbar/list.d.ts.map +1 -1
- package/build-types/style-provider/test/index.d.ts +2 -0
- package/build-types/style-provider/test/index.d.ts.map +1 -0
- package/build-types/toggle-group-control/toggle-group-control-option-base/styles.d.ts.map +1 -1
- package/build-types/ui/context/constants.d.ts +8 -0
- package/build-types/ui/context/constants.d.ts.map +1 -0
- package/build-types/ui/context/context-connect.d.ts +40 -0
- package/build-types/ui/context/context-connect.d.ts.map +1 -0
- package/build-types/ui/context/context-system-provider.d.ts +7 -0
- package/build-types/ui/context/context-system-provider.d.ts.map +1 -0
- package/build-types/ui/context/get-styled-class-name-from-key.d.ts +2 -0
- package/build-types/ui/context/get-styled-class-name-from-key.d.ts.map +1 -0
- package/build-types/ui/context/index.d.ts +5 -0
- package/build-types/ui/context/index.d.ts.map +1 -0
- package/build-types/ui/context/test/context-connect.d.ts +2 -0
- package/build-types/ui/context/test/context-connect.d.ts.map +1 -0
- package/build-types/ui/context/test/wordpress-component.d.ts +2 -0
- package/build-types/ui/context/test/wordpress-component.d.ts.map +1 -0
- package/build-types/ui/context/use-context-system.d.ts +20 -0
- package/build-types/ui/context/use-context-system.d.ts.map +1 -0
- package/build-types/ui/context/utils.d.ts +26 -0
- package/build-types/ui/context/utils.d.ts.map +1 -0
- package/build-types/ui/context/wordpress-component.d.ts +32 -0
- package/build-types/ui/context/wordpress-component.d.ts.map +1 -0
- package/build-types/ui/shortcut/component.d.ts +12 -0
- package/build-types/ui/shortcut/component.d.ts.map +1 -0
- package/build-types/ui/shortcut/index.d.ts +3 -0
- package/build-types/ui/shortcut/index.d.ts.map +1 -0
- package/build-types/ui/tooltip/component.d.ts +29 -0
- package/build-types/ui/tooltip/component.d.ts.map +1 -0
- package/build-types/ui/tooltip/content.d.ts +9 -0
- package/build-types/ui/tooltip/content.d.ts.map +1 -0
- package/build-types/ui/tooltip/context.d.ts +10 -0
- package/build-types/ui/tooltip/context.d.ts.map +1 -0
- package/build-types/ui/tooltip/index.d.ts +3 -0
- package/build-types/ui/tooltip/index.d.ts.map +1 -0
- package/build-types/ui/tooltip/styles.d.ts +12 -0
- package/build-types/ui/tooltip/styles.d.ts.map +1 -0
- package/build-types/ui/tooltip/types.d.ts +62 -0
- package/build-types/ui/tooltip/types.d.ts.map +1 -0
- package/build-types/ui/utils/colors.d.ts +17 -0
- package/build-types/ui/utils/colors.d.ts.map +1 -0
- package/build-types/ui/utils/font-size.d.ts +18 -0
- package/build-types/ui/utils/font-size.d.ts.map +1 -0
- package/build-types/ui/utils/get-valid-children.d.ts +13 -0
- package/build-types/ui/utils/get-valid-children.d.ts.map +1 -0
- package/build-types/ui/utils/index.d.ts +2 -0
- package/build-types/ui/utils/index.d.ts.map +1 -0
- package/build-types/ui/utils/space.d.ts +23 -0
- package/build-types/ui/utils/space.d.ts.map +1 -0
- package/build-types/ui/utils/types.d.ts +14 -0
- package/build-types/ui/utils/types.d.ts.map +1 -0
- package/build-types/ui/utils/use-responsive-value.d.ts +5 -0
- package/build-types/ui/utils/use-responsive-value.d.ts.map +1 -0
- package/build-types/utils/hooks/use-latest-ref.d.ts +15 -0
- package/build-types/utils/hooks/use-latest-ref.d.ts.map +1 -0
- package/build-types/utils/input/base.d.ts +3 -0
- package/build-types/utils/input/base.d.ts.map +1 -0
- package/build-types/utils/input/index.d.ts +2 -0
- package/build-types/utils/input/index.d.ts.map +1 -0
- package/build-types/utils/input/input-control.d.ts +2 -0
- package/build-types/utils/input/input-control.d.ts.map +1 -0
- package/build-types/utils/reduce-motion.d.ts +8 -0
- package/build-types/utils/reduce-motion.d.ts.map +1 -0
- package/build-types/utils/z-index.d.ts +3 -0
- package/build-types/utils/z-index.d.ts.map +1 -0
- package/build-types/validated-form-controls/control-with-error.d.ts.map +1 -1
- package/build-types/validated-form-controls/test/checkbox-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/checkbox-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/combobox-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/combobox-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/custom-select-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/custom-select-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/form-token-field.d.ts +2 -0
- package/build-types/validated-form-controls/test/form-token-field.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/input-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/input-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/number-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/number-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/radio-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/radio-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/range-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/range-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/select-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/select-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/text-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/text-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/textarea-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/textarea-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/toggle-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/toggle-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/test/toggle-group-control.d.ts +2 -0
- package/build-types/validated-form-controls/test/toggle-group-control.d.ts.map +1 -0
- package/build-types/validated-form-controls/validity-indicator.d.ts +2 -1
- package/build-types/validated-form-controls/validity-indicator.d.ts.map +1 -1
- package/build-types/view/types.d.ts +8 -0
- package/build-types/view/types.d.ts.map +1 -0
- package/package.json +21 -21
- package/src/autocomplete/index.tsx +30 -6
- package/src/autocomplete/test/index.tsx +110 -1
- package/src/button/style.scss +8 -5
- package/src/date-time/test/utils.test.ts +8 -11
- package/src/date-time/time/index.tsx +2 -12
- package/src/date-time/time/test/index.tsx +69 -0
- package/src/date-time/utils.ts +18 -0
- package/src/form-token-field/style.scss +1 -0
- package/src/private-apis.ts +0 -4
- package/src/sandbox/index.tsx +189 -9
- package/src/sandbox/test/index.tsx +65 -24
- package/src/sandbox/types.ts +11 -0
- package/src/select-control/styles/select-control-styles.ts +1 -0
- package/src/snackbar/list.tsx +6 -1
- package/src/style.scss +0 -2
- package/src/toggle-group-control/test/__snapshots__/index.tsx.snap +18 -16
- package/src/toggle-group-control/toggle-group-control-option-base/styles.ts +4 -6
- package/src/validated-form-controls/control-with-error.tsx +44 -4
- package/src/validated-form-controls/test/checkbox-control.tsx +49 -0
- package/src/validated-form-controls/test/combobox-control.tsx +61 -0
- package/src/validated-form-controls/test/control-with-error.tsx +182 -1
- package/src/validated-form-controls/test/custom-select-control.tsx +60 -0
- package/src/validated-form-controls/test/form-token-field.tsx +52 -0
- package/src/validated-form-controls/test/input-control.tsx +42 -0
- package/src/validated-form-controls/test/number-control.tsx +44 -0
- package/src/validated-form-controls/test/radio-control.tsx +61 -0
- package/src/validated-form-controls/test/range-control.tsx +73 -0
- package/src/validated-form-controls/test/select-control.tsx +57 -0
- package/src/validated-form-controls/test/text-control.tsx +49 -0
- package/src/validated-form-controls/test/textarea-control.tsx +51 -0
- package/src/validated-form-controls/test/toggle-control.tsx +49 -0
- package/src/validated-form-controls/test/toggle-group-control.tsx +28 -0
- package/src/validated-form-controls/validity-indicator.tsx +3 -0
- package/build/avatar/component.cjs +0 -99
- package/build/avatar/component.cjs.map +0 -7
- package/build/avatar/index.cjs +0 -37
- package/build/avatar/index.cjs.map +0 -7
- package/build/avatar/types.cjs +0 -19
- package/build/avatar/types.cjs.map +0 -7
- package/build/avatar-group/component.cjs +0 -60
- package/build/avatar-group/component.cjs.map +0 -7
- package/build/avatar-group/index.cjs +0 -37
- package/build/avatar-group/index.cjs.map +0 -7
- package/build/avatar-group/types.cjs +0 -19
- package/build/avatar-group/types.cjs.map +0 -7
- package/build-module/avatar/component.mjs +0 -68
- package/build-module/avatar/component.mjs.map +0 -7
- package/build-module/avatar/index.mjs +0 -6
- package/build-module/avatar/index.mjs.map +0 -7
- package/build-module/avatar/types.mjs +0 -1
- package/build-module/avatar/types.mjs.map +0 -7
- package/build-module/avatar-group/component.mjs +0 -29
- package/build-module/avatar-group/component.mjs.map +0 -7
- package/build-module/avatar-group/index.mjs +0 -6
- package/build-module/avatar-group/index.mjs.map +0 -7
- package/build-module/avatar-group/types.mjs +0 -1
- package/build-module/avatar-group/types.mjs.map +0 -7
- package/build-types/avatar/component.d.ts +0 -5
- package/build-types/avatar/component.d.ts.map +0 -1
- package/build-types/avatar/index.d.ts +0 -3
- package/build-types/avatar/index.d.ts.map +0 -1
- package/build-types/avatar/types.d.ts +0 -59
- package/build-types/avatar/types.d.ts.map +0 -1
- package/build-types/avatar-group/component.d.ts +0 -8
- package/build-types/avatar-group/component.d.ts.map +0 -1
- package/build-types/avatar-group/index.d.ts +0 -3
- package/build-types/avatar-group/index.d.ts.map +0 -1
- package/build-types/avatar-group/types.d.ts +0 -14
- package/build-types/avatar-group/types.d.ts.map +0 -1
- package/src/avatar/component.tsx +0 -82
- package/src/avatar/index.ts +0 -2
- package/src/avatar/styles.scss +0 -152
- package/src/avatar/types.ts +0 -59
- package/src/avatar-group/component.tsx +0 -46
- package/src/avatar-group/index.ts +0 -2
- package/src/avatar-group/styles.scss +0 -32
- package/src/avatar-group/types.ts +0 -13
package/src/sandbox/index.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
useRef,
|
|
7
7
|
useState,
|
|
8
8
|
useEffect,
|
|
9
|
+
useMemo,
|
|
9
10
|
} from '@wordpress/element';
|
|
10
11
|
import { useFocusableIframe, useMergeRefs } from '@wordpress/compose';
|
|
11
12
|
|
|
@@ -14,6 +15,8 @@ import { useFocusableIframe, useMergeRefs } from '@wordpress/compose';
|
|
|
14
15
|
*/
|
|
15
16
|
import type { SandBoxProps } from './types';
|
|
16
17
|
|
|
18
|
+
type SandBoxContentProps = Omit< SandBoxProps, 'allowSameOrigin' >;
|
|
19
|
+
|
|
17
20
|
const observeAndResizeJS = function () {
|
|
18
21
|
const { MutationObserver } = window;
|
|
19
22
|
|
|
@@ -115,17 +118,176 @@ const style = `
|
|
|
115
118
|
`;
|
|
116
119
|
|
|
117
120
|
/**
|
|
118
|
-
*
|
|
121
|
+
* Builds the full HTML document string for the sandbox iframe content.
|
|
122
|
+
*/
|
|
123
|
+
function buildSandBoxDocument( {
|
|
124
|
+
html,
|
|
125
|
+
title,
|
|
126
|
+
type,
|
|
127
|
+
styles,
|
|
128
|
+
scripts,
|
|
129
|
+
}: {
|
|
130
|
+
html: string;
|
|
131
|
+
title: string;
|
|
132
|
+
type?: string;
|
|
133
|
+
styles: string[];
|
|
134
|
+
scripts: string[];
|
|
135
|
+
} ): string {
|
|
136
|
+
const htmlDoc = (
|
|
137
|
+
<html lang={ document.documentElement.lang } className={ type }>
|
|
138
|
+
<head>
|
|
139
|
+
<title>{ title }</title>
|
|
140
|
+
<style dangerouslySetInnerHTML={ { __html: style } } />
|
|
141
|
+
{ styles.map( ( rules, i ) => (
|
|
142
|
+
<style
|
|
143
|
+
key={ i }
|
|
144
|
+
dangerouslySetInnerHTML={ { __html: rules } }
|
|
145
|
+
/>
|
|
146
|
+
) ) }
|
|
147
|
+
</head>
|
|
148
|
+
<body
|
|
149
|
+
data-resizable-iframe-connected="data-resizable-iframe-connected"
|
|
150
|
+
className={ type }
|
|
151
|
+
>
|
|
152
|
+
<div dangerouslySetInnerHTML={ { __html: html } } />
|
|
153
|
+
<script
|
|
154
|
+
type="text/javascript"
|
|
155
|
+
dangerouslySetInnerHTML={ {
|
|
156
|
+
__html: `(${ observeAndResizeJS.toString() })();`,
|
|
157
|
+
} }
|
|
158
|
+
/>
|
|
159
|
+
{ scripts.map( ( src ) => (
|
|
160
|
+
<script key={ src } src={ src } />
|
|
161
|
+
) ) }
|
|
162
|
+
</body>
|
|
163
|
+
</html>
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return '<!DOCTYPE html>' + renderToString( htmlDoc );
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Isolated sandbox that uses the `srcdoc` attribute to render content
|
|
171
|
+
* without `allow-same-origin`. This is the default for user-controlled
|
|
172
|
+
* content (e.g., the HTML block) where same-origin access would be a
|
|
173
|
+
* security risk.
|
|
119
174
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
175
|
+
* Because `srcdoc` is a declarative attribute, the browser automatically
|
|
176
|
+
* re-renders the content when the iframe is moved in the DOM (e.g.,
|
|
177
|
+
* block reordering), so no `load` event listener is needed.
|
|
178
|
+
* The `message` listener is re-synced on every `load` so
|
|
179
|
+
* it follows the iframe if it's reparented into a different document.
|
|
180
|
+
*/
|
|
181
|
+
function IsolatedSandBox( {
|
|
182
|
+
html = '',
|
|
183
|
+
title = '',
|
|
184
|
+
type,
|
|
185
|
+
styles = [],
|
|
186
|
+
scripts = [],
|
|
187
|
+
onFocus,
|
|
188
|
+
tabIndex,
|
|
189
|
+
}: SandBoxContentProps ) {
|
|
190
|
+
const ref = useRef< HTMLIFrameElement >( null );
|
|
191
|
+
const [ width, setWidth ] = useState( 0 );
|
|
192
|
+
const [ height, setHeight ] = useState( 0 );
|
|
193
|
+
|
|
194
|
+
const srcDoc = useMemo(
|
|
195
|
+
() => buildSandBoxDocument( { html, title, type, styles, scripts } ),
|
|
196
|
+
[ html, title, type, styles, scripts ]
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
useEffect( () => {
|
|
200
|
+
const iframe = ref.current;
|
|
201
|
+
if ( ! iframe ) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function checkMessageForResize( event: MessageEvent ) {
|
|
206
|
+
// Verify that the mounted element is the source of the message.
|
|
207
|
+
// iframe.contentWindow is accessible cross-origin as a
|
|
208
|
+
// WindowProxy reference, so this check still works without
|
|
209
|
+
// allow-same-origin.
|
|
210
|
+
if ( ! iframe || iframe.contentWindow !== event.source ) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Attempt to parse the message data as JSON if passed as string.
|
|
215
|
+
let data = event.data || {};
|
|
216
|
+
|
|
217
|
+
if ( 'string' === typeof data ) {
|
|
218
|
+
try {
|
|
219
|
+
data = JSON.parse( data );
|
|
220
|
+
} catch {}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Update the state only if the message is formatted as we expect,
|
|
224
|
+
// i.e. as an object with a 'resize' action.
|
|
225
|
+
if ( 'resize' !== data.action ) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
setWidth( data.width );
|
|
230
|
+
setHeight( data.height );
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let currentView: Window | null = null;
|
|
234
|
+
function syncListener() {
|
|
235
|
+
const view = iframe?.ownerDocument?.defaultView ?? null;
|
|
236
|
+
if ( view === currentView ) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
currentView?.removeEventListener(
|
|
241
|
+
'message',
|
|
242
|
+
checkMessageForResize
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
currentView = view;
|
|
246
|
+
currentView?.addEventListener( 'message', checkMessageForResize );
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
syncListener();
|
|
250
|
+
iframe.addEventListener( 'load', syncListener );
|
|
251
|
+
|
|
252
|
+
return () => {
|
|
253
|
+
iframe.removeEventListener( 'load', syncListener );
|
|
254
|
+
currentView?.removeEventListener(
|
|
255
|
+
'message',
|
|
256
|
+
checkMessageForResize
|
|
257
|
+
);
|
|
258
|
+
};
|
|
259
|
+
}, [] );
|
|
260
|
+
|
|
261
|
+
return (
|
|
262
|
+
<iframe
|
|
263
|
+
ref={ useMergeRefs( [ ref, useFocusableIframe() ] ) }
|
|
264
|
+
title={ title }
|
|
265
|
+
tabIndex={ tabIndex }
|
|
266
|
+
className="components-sandbox"
|
|
267
|
+
sandbox="allow-scripts allow-presentation"
|
|
268
|
+
srcDoc={ srcDoc }
|
|
269
|
+
onFocus={ onFocus }
|
|
270
|
+
width={ Math.ceil( width ) }
|
|
271
|
+
height={ Math.ceil( height ) }
|
|
272
|
+
/>
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Same-origin sandbox that writes to `contentDocument` directly. This
|
|
278
|
+
* preserves the parent page's URL as the iframe's document URL, which
|
|
279
|
+
* provides a valid Referer header for nested iframes (required by
|
|
280
|
+
* providers like YouTube).
|
|
122
281
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
282
|
+
* Only used when `allowSameOrigin` is true — i.e., for server-fetched
|
|
283
|
+
* oEmbed previews that are not directly user-controlled.
|
|
284
|
+
*
|
|
285
|
+
* This implementation is intentionally kept close to the original
|
|
286
|
+
* pre-refactor code to preserve past bugfixes:
|
|
287
|
+
* - load listener for iframe re-initialization after DOM moves (#21916)
|
|
288
|
+
* - forceRerender guard to avoid unnecessary full rewrites (#20176)
|
|
127
289
|
*/
|
|
128
|
-
function
|
|
290
|
+
function SameOriginSandBox( {
|
|
129
291
|
html = '',
|
|
130
292
|
title = '',
|
|
131
293
|
type,
|
|
@@ -133,7 +295,7 @@ function SandBox( {
|
|
|
133
295
|
scripts = [],
|
|
134
296
|
onFocus,
|
|
135
297
|
tabIndex,
|
|
136
|
-
}:
|
|
298
|
+
}: SandBoxContentProps ) {
|
|
137
299
|
const ref = useRef< HTMLIFrameElement >( null );
|
|
138
300
|
const [ width, setWidth ] = useState( 0 );
|
|
139
301
|
const [ height, setHeight ] = useState( 0 );
|
|
@@ -292,4 +454,22 @@ function SandBox( {
|
|
|
292
454
|
);
|
|
293
455
|
}
|
|
294
456
|
|
|
457
|
+
/**
|
|
458
|
+
* This component provides an isolated environment for arbitrary HTML via iframes.
|
|
459
|
+
*
|
|
460
|
+
* ```jsx
|
|
461
|
+
* import { SandBox } from '@wordpress/components';
|
|
462
|
+
*
|
|
463
|
+
* const MySandBox = () => (
|
|
464
|
+
* <SandBox html="<p>Content</p>" title="SandBox" type="embed" />
|
|
465
|
+
* );
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
function SandBox( { allowSameOrigin = false, ...contentProps }: SandBoxProps ) {
|
|
469
|
+
if ( allowSameOrigin ) {
|
|
470
|
+
return <SameOriginSandBox { ...contentProps } />;
|
|
471
|
+
}
|
|
472
|
+
return <IsolatedSandBox { ...contentProps } />;
|
|
473
|
+
}
|
|
474
|
+
|
|
295
475
|
export default SandBox;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import { fireEvent, render, screen
|
|
4
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* WordPress dependencies
|
|
@@ -16,10 +16,7 @@ import SandBox from '..';
|
|
|
16
16
|
describe( 'SandBox', () => {
|
|
17
17
|
const TestWrapper = () => {
|
|
18
18
|
const [ html, setHtml ] = useState(
|
|
19
|
-
|
|
20
|
-
// with iframes so we need to ignore it for the time being.
|
|
21
|
-
'<script type="text/javascript">window.MutationObserver = null;</script>' +
|
|
22
|
-
'<iframe title="Mock Iframe" src="https://super.embed"></iframe>'
|
|
19
|
+
'<iframe title="Mock Iframe" src="https://super.embed"></iframe>'
|
|
23
20
|
);
|
|
24
21
|
|
|
25
22
|
const updateHtml = () => {
|
|
@@ -38,34 +35,78 @@ describe( 'SandBox', () => {
|
|
|
38
35
|
);
|
|
39
36
|
};
|
|
40
37
|
|
|
41
|
-
it( 'should
|
|
38
|
+
it( 'should not include allow-same-origin by default', () => {
|
|
39
|
+
render( <SandBox html="<p>Hello</p>" title="Test" /> );
|
|
40
|
+
|
|
41
|
+
const iframe = screen.getByTitle< HTMLIFrameElement >( 'Test' );
|
|
42
|
+
|
|
43
|
+
expect( iframe ).toHaveAttribute(
|
|
44
|
+
'sandbox',
|
|
45
|
+
'allow-scripts allow-presentation'
|
|
46
|
+
);
|
|
47
|
+
expect( iframe.getAttribute( 'sandbox' ) ).not.toContain(
|
|
48
|
+
'allow-same-origin'
|
|
49
|
+
);
|
|
50
|
+
} );
|
|
51
|
+
|
|
52
|
+
it( 'should set srcdoc with the provided html content', () => {
|
|
53
|
+
render( <SandBox html="<p>Hello</p>" title="Test Title" /> );
|
|
54
|
+
|
|
55
|
+
const iframe = screen.getByTitle< HTMLIFrameElement >( 'Test Title' );
|
|
56
|
+
const srcDoc = iframe.getAttribute( 'srcdoc' ) ?? '';
|
|
57
|
+
|
|
58
|
+
expect( srcDoc ).toContain( '<p>Hello</p>' );
|
|
59
|
+
expect( srcDoc ).toContain( '<title>Test Title</title>' );
|
|
60
|
+
} );
|
|
61
|
+
|
|
62
|
+
it( 'should include custom styles in srcdoc', () => {
|
|
63
|
+
render(
|
|
64
|
+
<SandBox
|
|
65
|
+
html="<p>Styled</p>"
|
|
66
|
+
title="Styled Test"
|
|
67
|
+
styles={ [ '.custom { color: red; }' ] }
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const iframe = screen.getByTitle< HTMLIFrameElement >( 'Styled Test' );
|
|
72
|
+
const srcDoc = iframe.getAttribute( 'srcdoc' ) ?? '';
|
|
73
|
+
|
|
74
|
+
expect( srcDoc ).toContain( '.custom { color: red; }' );
|
|
75
|
+
} );
|
|
76
|
+
|
|
77
|
+
it( 'should include script tags in srcdoc', () => {
|
|
78
|
+
render(
|
|
79
|
+
<SandBox
|
|
80
|
+
html="<p>Script</p>"
|
|
81
|
+
title="Script Test"
|
|
82
|
+
scripts={ [ 'https://example.com/embed.js' ] }
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const iframe = screen.getByTitle< HTMLIFrameElement >( 'Script Test' );
|
|
87
|
+
const srcDoc = iframe.getAttribute( 'srcdoc' ) ?? '';
|
|
88
|
+
|
|
89
|
+
expect( srcDoc ).toContain(
|
|
90
|
+
'<script src="https://example.com/embed.js">'
|
|
91
|
+
);
|
|
92
|
+
} );
|
|
93
|
+
|
|
94
|
+
it( 'should update srcdoc when html prop changes', () => {
|
|
42
95
|
render( <TestWrapper /> );
|
|
43
96
|
|
|
44
97
|
const iframe =
|
|
45
98
|
screen.getByTitle< HTMLIFrameElement >( 'SandBox Title' );
|
|
46
99
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
let sandboxedIframe = within(
|
|
52
|
-
iframe.contentWindow.document.body
|
|
53
|
-
).getByTitle( 'Mock Iframe' );
|
|
54
|
-
|
|
55
|
-
expect( sandboxedIframe ).toHaveAttribute(
|
|
56
|
-
'src',
|
|
57
|
-
'https://super.embed'
|
|
100
|
+
expect( iframe ).toHaveAttribute(
|
|
101
|
+
'srcdoc',
|
|
102
|
+
expect.stringContaining( 'https://super.embed' )
|
|
58
103
|
);
|
|
59
104
|
|
|
60
105
|
fireEvent.click( screen.getByRole( 'button' ) );
|
|
61
106
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
expect( sandboxedIframe ).toHaveAttribute(
|
|
67
|
-
'src',
|
|
68
|
-
'https://another.super.embed'
|
|
107
|
+
expect( iframe ).toHaveAttribute(
|
|
108
|
+
'srcdoc',
|
|
109
|
+
expect.stringContaining( 'https://another.super.embed' )
|
|
69
110
|
);
|
|
70
111
|
} );
|
|
71
112
|
} );
|
package/src/sandbox/types.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
export type SandBoxProps = {
|
|
2
|
+
/**
|
|
3
|
+
* Whether to include `allow-same-origin` in the iframe's sandbox
|
|
4
|
+
* attribute. When true, nested iframes (such as third-party embeds)
|
|
5
|
+
* can access their own origin's cookies and storage.
|
|
6
|
+
*
|
|
7
|
+
* Only enable this for content that is NOT directly user-controlled,
|
|
8
|
+
* such as server-fetched oEmbed previews.
|
|
9
|
+
*
|
|
10
|
+
* @default false
|
|
11
|
+
*/
|
|
12
|
+
allowSameOrigin?: boolean;
|
|
2
13
|
/**
|
|
3
14
|
* The HTML to render in the body of the iframe document.
|
|
4
15
|
*
|
|
@@ -162,6 +162,7 @@ export const Select = styled.select< SelectProps >`
|
|
|
162
162
|
cursor: inherit;
|
|
163
163
|
display: block;
|
|
164
164
|
font-family: inherit;
|
|
165
|
+
line-height: 1.3; // Override forms.css styles, large enough value to prevent descender clipping without affecting height
|
|
165
166
|
margin: 0;
|
|
166
167
|
width: 100%;
|
|
167
168
|
max-width: none;
|
package/src/snackbar/list.tsx
CHANGED
|
@@ -86,7 +86,12 @@ export function SnackbarList( {
|
|
|
86
86
|
|
|
87
87
|
return (
|
|
88
88
|
<motion.div
|
|
89
|
-
layout={
|
|
89
|
+
layout={ isReducedMotion ? false : 'position' } // See https://www.framer.com/docs/animation/#layout-animations
|
|
90
|
+
// Ensures a stable full-width bounding box so that
|
|
91
|
+
// `layout="position"` only ever detects vertical
|
|
92
|
+
// shifts, preventing horizontal animation when
|
|
93
|
+
// notice text changes in place.
|
|
94
|
+
style={ { width: '100%' } }
|
|
90
95
|
initial="init"
|
|
91
96
|
animate="open"
|
|
92
97
|
exit="exit"
|
package/src/style.scss
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
@use "@wordpress/base-styles/mixins" as *;
|
|
2
2
|
@use "./animate/style.scss" as *;
|
|
3
3
|
@use "./autocomplete/style.scss" as *;
|
|
4
|
-
@use "./avatar/styles.scss" as *;
|
|
5
|
-
@use "./avatar-group/styles.scss" as *;
|
|
6
4
|
@use "./badge/styles.scss" as *;
|
|
7
5
|
@use "./button-group/style.scss" as *;
|
|
8
6
|
@use "./button/style.scss" as *;
|
|
@@ -137,7 +137,6 @@ exports[`ToggleGroupControl controlled should render correctly with icons 1`] =
|
|
|
137
137
|
user-select: none;
|
|
138
138
|
width: 100%;
|
|
139
139
|
z-index: 2;
|
|
140
|
-
color: var(--wp-components-color-foreground, #1e1e1e);
|
|
141
140
|
height: 38px;
|
|
142
141
|
aspect-ratio: 1;
|
|
143
142
|
padding-left: 0;
|
|
@@ -157,12 +156,13 @@ exports[`ToggleGroupControl controlled should render correctly with icons 1`] =
|
|
|
157
156
|
border: 0;
|
|
158
157
|
}
|
|
159
158
|
|
|
160
|
-
.emotion-12[disabled]
|
|
159
|
+
.emotion-12[disabled],
|
|
160
|
+
.emotion-12[aria-disabled='true'] {
|
|
161
161
|
opacity: 0.4;
|
|
162
162
|
cursor: default;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
.emotion-12:hover {
|
|
165
|
+
.emotion-12:hover:not( [disabled] ):not( [aria-disabled='true'] ) {
|
|
166
166
|
color: var(--wp-components-color-foreground, #1e1e1e);
|
|
167
167
|
}
|
|
168
168
|
|
|
@@ -211,7 +211,6 @@ exports[`ToggleGroupControl controlled should render correctly with icons 1`] =
|
|
|
211
211
|
user-select: none;
|
|
212
212
|
width: 100%;
|
|
213
213
|
z-index: 2;
|
|
214
|
-
color: var(--wp-components-color-foreground, #1e1e1e);
|
|
215
214
|
height: 38px;
|
|
216
215
|
aspect-ratio: 1;
|
|
217
216
|
padding-left: 0;
|
|
@@ -229,12 +228,13 @@ exports[`ToggleGroupControl controlled should render correctly with icons 1`] =
|
|
|
229
228
|
border: 0;
|
|
230
229
|
}
|
|
231
230
|
|
|
232
|
-
.emotion-17[disabled]
|
|
231
|
+
.emotion-17[disabled],
|
|
232
|
+
.emotion-17[aria-disabled='true'] {
|
|
233
233
|
opacity: 0.4;
|
|
234
234
|
cursor: default;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
.emotion-17:hover {
|
|
237
|
+
.emotion-17:hover:not( [disabled] ):not( [aria-disabled='true'] ) {
|
|
238
238
|
color: var(--wp-components-color-foreground, #1e1e1e);
|
|
239
239
|
}
|
|
240
240
|
|
|
@@ -491,12 +491,13 @@ exports[`ToggleGroupControl controlled should render correctly with text options
|
|
|
491
491
|
border: 0;
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
-
.emotion-12[disabled]
|
|
494
|
+
.emotion-12[disabled],
|
|
495
|
+
.emotion-12[aria-disabled='true'] {
|
|
495
496
|
opacity: 0.4;
|
|
496
497
|
cursor: default;
|
|
497
498
|
}
|
|
498
499
|
|
|
499
|
-
.emotion-12:hover {
|
|
500
|
+
.emotion-12:hover:not( [disabled] ):not( [aria-disabled='true'] ) {
|
|
500
501
|
color: var(--wp-components-color-foreground, #1e1e1e);
|
|
501
502
|
}
|
|
502
503
|
|
|
@@ -726,7 +727,6 @@ exports[`ToggleGroupControl uncontrolled should render correctly with icons 1`]
|
|
|
726
727
|
user-select: none;
|
|
727
728
|
width: 100%;
|
|
728
729
|
z-index: 2;
|
|
729
|
-
color: var(--wp-components-color-foreground, #1e1e1e);
|
|
730
730
|
height: 38px;
|
|
731
731
|
aspect-ratio: 1;
|
|
732
732
|
padding-left: 0;
|
|
@@ -746,12 +746,13 @@ exports[`ToggleGroupControl uncontrolled should render correctly with icons 1`]
|
|
|
746
746
|
border: 0;
|
|
747
747
|
}
|
|
748
748
|
|
|
749
|
-
.emotion-12[disabled]
|
|
749
|
+
.emotion-12[disabled],
|
|
750
|
+
.emotion-12[aria-disabled='true'] {
|
|
750
751
|
opacity: 0.4;
|
|
751
752
|
cursor: default;
|
|
752
753
|
}
|
|
753
754
|
|
|
754
|
-
.emotion-12:hover {
|
|
755
|
+
.emotion-12:hover:not( [disabled] ):not( [aria-disabled='true'] ) {
|
|
755
756
|
color: var(--wp-components-color-foreground, #1e1e1e);
|
|
756
757
|
}
|
|
757
758
|
|
|
@@ -800,7 +801,6 @@ exports[`ToggleGroupControl uncontrolled should render correctly with icons 1`]
|
|
|
800
801
|
user-select: none;
|
|
801
802
|
width: 100%;
|
|
802
803
|
z-index: 2;
|
|
803
|
-
color: var(--wp-components-color-foreground, #1e1e1e);
|
|
804
804
|
height: 38px;
|
|
805
805
|
aspect-ratio: 1;
|
|
806
806
|
padding-left: 0;
|
|
@@ -818,12 +818,13 @@ exports[`ToggleGroupControl uncontrolled should render correctly with icons 1`]
|
|
|
818
818
|
border: 0;
|
|
819
819
|
}
|
|
820
820
|
|
|
821
|
-
.emotion-17[disabled]
|
|
821
|
+
.emotion-17[disabled],
|
|
822
|
+
.emotion-17[aria-disabled='true'] {
|
|
822
823
|
opacity: 0.4;
|
|
823
824
|
cursor: default;
|
|
824
825
|
}
|
|
825
826
|
|
|
826
|
-
.emotion-17:hover {
|
|
827
|
+
.emotion-17:hover:not( [disabled] ):not( [aria-disabled='true'] ) {
|
|
827
828
|
color: var(--wp-components-color-foreground, #1e1e1e);
|
|
828
829
|
}
|
|
829
830
|
|
|
@@ -1074,12 +1075,13 @@ exports[`ToggleGroupControl uncontrolled should render correctly with text optio
|
|
|
1074
1075
|
border: 0;
|
|
1075
1076
|
}
|
|
1076
1077
|
|
|
1077
|
-
.emotion-12[disabled]
|
|
1078
|
+
.emotion-12[disabled],
|
|
1079
|
+
.emotion-12[aria-disabled='true'] {
|
|
1078
1080
|
opacity: 0.4;
|
|
1079
1081
|
cursor: default;
|
|
1080
1082
|
}
|
|
1081
1083
|
|
|
1082
|
-
.emotion-12:hover {
|
|
1084
|
+
.emotion-12:hover:not( [disabled] ):not( [aria-disabled='true'] ) {
|
|
1083
1085
|
color: var(--wp-components-color-foreground, #1e1e1e);
|
|
1084
1086
|
}
|
|
1085
1087
|
|
|
@@ -63,13 +63,14 @@ export const buttonView = ( {
|
|
|
63
63
|
border: 0;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
&[disabled]
|
|
66
|
+
&[disabled],
|
|
67
|
+
&[aria-disabled='true'] {
|
|
67
68
|
opacity: 0.4;
|
|
68
69
|
cursor: default;
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
&:hover {
|
|
72
|
-
color: ${ COLORS.theme.
|
|
72
|
+
&:hover:not( [disabled] ):not( [aria-disabled='true'] ) {
|
|
73
|
+
color: ${ COLORS.theme.foreground };
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
${ isDeselectable && deselectable }
|
|
@@ -83,8 +84,6 @@ const pressed = css`
|
|
|
83
84
|
`;
|
|
84
85
|
|
|
85
86
|
const deselectable = css`
|
|
86
|
-
color: ${ COLORS.theme.foreground };
|
|
87
|
-
|
|
88
87
|
&:focus {
|
|
89
88
|
outline: ${ CONFIG.borderWidthFocus } solid ${ COLORS.ui.borderFocus };
|
|
90
89
|
outline-offset: 2px;
|
|
@@ -112,7 +111,6 @@ const isIconStyles = ( {
|
|
|
112
111
|
};
|
|
113
112
|
|
|
114
113
|
return css`
|
|
115
|
-
color: ${ COLORS.theme.foreground };
|
|
116
114
|
height: ${ iconButtonSizes[ size ] };
|
|
117
115
|
aspect-ratio: 1;
|
|
118
116
|
padding-left: 0;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
cloneElement,
|
|
7
7
|
forwardRef,
|
|
8
8
|
useEffect,
|
|
9
|
+
useId,
|
|
9
10
|
useState,
|
|
10
11
|
} from '@wordpress/element';
|
|
11
12
|
|
|
@@ -238,22 +239,61 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
|
|
|
238
239
|
}
|
|
239
240
|
};
|
|
240
241
|
|
|
241
|
-
const
|
|
242
|
+
const messageId = useId();
|
|
243
|
+
|
|
244
|
+
const message = ( () => {
|
|
242
245
|
if ( errorMessage ) {
|
|
243
246
|
return (
|
|
244
|
-
<ValidityIndicator
|
|
247
|
+
<ValidityIndicator
|
|
248
|
+
id={ messageId }
|
|
249
|
+
type="invalid"
|
|
250
|
+
message={ errorMessage }
|
|
251
|
+
/>
|
|
245
252
|
);
|
|
246
253
|
}
|
|
247
254
|
if ( statusMessage?.type ) {
|
|
248
255
|
return (
|
|
249
256
|
<ValidityIndicator
|
|
257
|
+
id={ messageId }
|
|
250
258
|
type={ statusMessage.type }
|
|
251
259
|
message={ statusMessage.message }
|
|
252
260
|
/>
|
|
253
261
|
);
|
|
254
262
|
}
|
|
255
263
|
return null;
|
|
256
|
-
};
|
|
264
|
+
} )();
|
|
265
|
+
|
|
266
|
+
const visibleMessage = showMessage ? message : null;
|
|
267
|
+
|
|
268
|
+
// Imperatively manage `aria-describedby` on the validity target so we
|
|
269
|
+
// merge with any value the child control sets internally (e.g. from a
|
|
270
|
+
// `help` prop), rather than competing with it at the props level.
|
|
271
|
+
useEffect( () => {
|
|
272
|
+
const target = getValidityTarget();
|
|
273
|
+
if ( ! target ) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function setDescribedBy( el: Element, shouldAdd: boolean ) {
|
|
278
|
+
const ids = ( el.getAttribute( 'aria-describedby' ) ?? '' )
|
|
279
|
+
.split( ' ' )
|
|
280
|
+
.filter( ( id ) => id && id !== messageId );
|
|
281
|
+
|
|
282
|
+
if ( shouldAdd ) {
|
|
283
|
+
ids.push( messageId );
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if ( ids.length ) {
|
|
287
|
+
el.setAttribute( 'aria-describedby', ids.join( ' ' ) );
|
|
288
|
+
} else {
|
|
289
|
+
el.removeAttribute( 'aria-describedby' );
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
setDescribedBy( target, !! visibleMessage );
|
|
294
|
+
|
|
295
|
+
return () => setDescribedBy( target, false );
|
|
296
|
+
}, [ visibleMessage, messageId, getValidityTarget ] );
|
|
257
297
|
|
|
258
298
|
return (
|
|
259
299
|
<div className={ className } ref={ forwardedRef } onBlur={ onBlur }>
|
|
@@ -265,7 +305,7 @@ function UnforwardedControlWithError< C extends React.ReactElement >(
|
|
|
265
305
|
),
|
|
266
306
|
required,
|
|
267
307
|
} ) }
|
|
268
|
-
<div aria-live="polite">{
|
|
308
|
+
<div aria-live="polite">{ visibleMessage }</div>
|
|
269
309
|
</div>
|
|
270
310
|
);
|
|
271
311
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { ValidatedCheckboxControl } from '../components';
|
|
4
|
+
|
|
5
|
+
describe( 'ValidatedCheckboxControl', () => {
|
|
6
|
+
it( 'should preserve the help description', () => {
|
|
7
|
+
render(
|
|
8
|
+
<ValidatedCheckboxControl
|
|
9
|
+
label="Agree"
|
|
10
|
+
help="You must agree to continue."
|
|
11
|
+
onChange={ () => {} }
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
expect(
|
|
16
|
+
screen.getByRole( 'checkbox', { name: 'Agree' } )
|
|
17
|
+
).toHaveAccessibleDescription( 'You must agree to continue.' );
|
|
18
|
+
} );
|
|
19
|
+
|
|
20
|
+
it( 'should append the validation error alongside the help description', async () => {
|
|
21
|
+
const user = userEvent.setup();
|
|
22
|
+
render(
|
|
23
|
+
<form>
|
|
24
|
+
<ValidatedCheckboxControl
|
|
25
|
+
label="Agree"
|
|
26
|
+
help="You must agree to continue."
|
|
27
|
+
onChange={ () => {} }
|
|
28
|
+
required
|
|
29
|
+
/>
|
|
30
|
+
<button type="submit">Submit</button>
|
|
31
|
+
</form>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const checkbox = screen.getByRole( 'checkbox', {
|
|
35
|
+
name: /^Agree/,
|
|
36
|
+
} );
|
|
37
|
+
|
|
38
|
+
await user.click( screen.getByRole( 'button', { name: 'Submit' } ) );
|
|
39
|
+
|
|
40
|
+
await waitFor( () => {
|
|
41
|
+
expect( checkbox ).toHaveAccessibleDescription(
|
|
42
|
+
expect.stringContaining( 'Constraints not satisfied' )
|
|
43
|
+
);
|
|
44
|
+
} );
|
|
45
|
+
expect( checkbox ).toHaveAccessibleDescription(
|
|
46
|
+
expect.stringContaining( 'You must agree to continue.' )
|
|
47
|
+
);
|
|
48
|
+
} );
|
|
49
|
+
} );
|