@wordpress/components 28.13.1-next.082ed6819.0 → 29.0.1-next.a9f418477.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 +51 -2
- package/CONTRIBUTING.md +16 -16
- package/build/angle-picker-control/index.js +1 -1
- package/build/angle-picker-control/index.js.map +1 -1
- package/build/animation/index.js +0 -6
- package/build/animation/index.js.map +1 -1
- package/build/autocomplete/index.js +0 -1
- package/build/autocomplete/index.js.map +1 -1
- package/build/border-box-control/border-box-control-split-controls/component.js +1 -1
- package/build/border-box-control/border-box-control-split-controls/component.js.map +1 -1
- package/build/border-control/border-control/component.js +2 -0
- package/build/border-control/border-control/component.js.map +1 -1
- package/build/box-control/all-input-control.js +1 -0
- package/build/box-control/all-input-control.js.map +1 -1
- package/build/box-control/axial-input-controls.js +1 -0
- package/build/box-control/axial-input-controls.js.map +1 -1
- package/build/box-control/index.js +22 -15
- package/build/box-control/index.js.map +1 -1
- package/build/box-control/input-controls.js +1 -0
- package/build/box-control/input-controls.js.map +1 -1
- package/build/box-control/types.js.map +1 -1
- package/build/circular-option-picker/circular-option-picker-actions.js +1 -0
- package/build/circular-option-picker/circular-option-picker-actions.js.map +1 -1
- package/build/circular-option-picker/circular-option-picker-option.js +1 -0
- package/build/circular-option-picker/circular-option-picker-option.js.map +1 -1
- package/build/clipboard-button/index.js +5 -3
- package/build/clipboard-button/index.js.map +1 -1
- package/build/color-palette/index.native.js +0 -1
- package/build/color-palette/index.native.js.map +1 -1
- package/build/color-picker/input-with-slider.js +2 -2
- package/build/color-picker/input-with-slider.js.map +1 -1
- package/build/composite/item.js +0 -9
- package/build/composite/item.js.map +1 -1
- package/build/context/context-connect.js +0 -1
- package/build/context/context-connect.js.map +1 -1
- package/build/custom-gradient-picker/gradient-bar/control-points.js +2 -0
- package/build/custom-gradient-picker/gradient-bar/control-points.js.map +1 -1
- package/build/custom-gradient-picker/index.js +2 -0
- package/build/custom-gradient-picker/index.js.map +1 -1
- package/build/custom-gradient-picker/types.js.map +1 -1
- package/build/custom-select-control/index.js +8 -0
- package/build/custom-select-control/index.js.map +1 -1
- package/build/custom-select-control/types.js.map +1 -1
- package/build/drop-zone/index.js +19 -13
- package/build/drop-zone/index.js.map +1 -1
- package/build/drop-zone/types.js.map +1 -1
- package/build/dropdown-menu/index.js +1 -0
- package/build/dropdown-menu/index.js.map +1 -1
- package/build/duotone-picker/color-list-picker/index.js +9 -14
- package/build/duotone-picker/color-list-picker/index.js.map +1 -1
- package/build/external-link/index.js +0 -1
- package/build/external-link/index.js.map +1 -1
- package/build/font-size-picker/font-size-picker-select.js +1 -0
- package/build/font-size-picker/font-size-picker-select.js.map +1 -1
- package/build/font-size-picker/index.js +1 -0
- package/build/font-size-picker/index.js.map +1 -1
- package/build/form-file-upload/index.js +11 -1
- package/build/form-file-upload/index.js.map +1 -1
- package/build/form-file-upload/types.js.map +1 -1
- package/build/form-token-field/index.js +6 -1
- package/build/form-token-field/index.js.map +1 -1
- package/build/gradient-picker/index.js +2 -0
- package/build/gradient-picker/index.js.map +1 -1
- package/build/gradient-picker/types.js.map +1 -1
- package/build/icon/index.js +9 -0
- package/build/icon/index.js.map +1 -1
- package/build/index.js +0 -6
- package/build/index.js.map +1 -1
- package/build/menu/checkbox-item.js +5 -9
- package/build/menu/checkbox-item.js.map +1 -1
- package/build/menu/group-label.js +4 -1
- package/build/menu/group-label.js.map +1 -1
- package/build/menu/group.js +4 -1
- package/build/menu/group.js.map +1 -1
- package/build/menu/item-help-text.js +5 -0
- package/build/menu/item-help-text.js.map +1 -1
- package/build/menu/item-label.js +5 -0
- package/build/menu/item-label.js.map +1 -1
- package/build/menu/item.js +4 -8
- package/build/menu/item.js.map +1 -1
- package/build/menu/radio-item.js +5 -9
- package/build/menu/radio-item.js.map +1 -1
- package/build/menu/separator.js +5 -2
- package/build/menu/separator.js.map +1 -1
- package/build/modal/aria-helper.js +0 -1
- package/build/modal/aria-helper.js.map +1 -1
- package/build/modal/index.js +0 -1
- package/build/modal/index.js.map +1 -1
- package/build/number-control/index.js +8 -0
- package/build/number-control/index.js.map +1 -1
- package/build/number-control/types.js.map +1 -1
- package/build/range-control/index.js +2 -1
- package/build/range-control/index.js.map +1 -1
- package/build/range-control/mark.js +0 -1
- package/build/range-control/mark.js.map +1 -1
- package/build/range-control/styles/range-control-styles.js +33 -45
- package/build/range-control/styles/range-control-styles.js.map +1 -1
- package/build/resizable-box/index.js +9 -1
- package/build/resizable-box/index.js.map +1 -1
- package/build/slot-fill/bubbles-virtually/fill.js +8 -12
- package/build/slot-fill/bubbles-virtually/fill.js.map +1 -1
- package/build/slot-fill/bubbles-virtually/slot-fill-provider.js +6 -10
- package/build/slot-fill/bubbles-virtually/slot-fill-provider.js.map +1 -1
- package/build/slot-fill/bubbles-virtually/slot.js +4 -10
- package/build/slot-fill/bubbles-virtually/slot.js.map +1 -1
- package/build/slot-fill/types.js.map +1 -1
- package/build/style-provider/index.js +0 -1
- package/build/style-provider/index.js.map +1 -1
- package/build/tabs/tab.js +0 -17
- package/build/tabs/tab.js.map +1 -1
- package/build/toolbar/toolbar-button/index.js +2 -0
- package/build/toolbar/toolbar-button/index.js.map +1 -1
- package/build/tools-panel/tools-panel/component.js +2 -0
- package/build/tools-panel/tools-panel/component.js.map +1 -1
- package/build/tree-grid/cell.js +4 -1
- package/build/tree-grid/cell.js.map +1 -1
- package/build/tree-grid/types.js.map +1 -1
- package/build/unit-control/index.js +10 -1
- package/build/unit-control/index.js.map +1 -1
- package/build/unit-control/types.js.map +1 -1
- package/build-module/angle-picker-control/index.js +1 -1
- package/build-module/angle-picker-control/index.js.map +1 -1
- package/build-module/animation/index.js +1 -1
- package/build-module/animation/index.js.map +1 -1
- package/build-module/autocomplete/index.js +0 -1
- package/build-module/autocomplete/index.js.map +1 -1
- package/build-module/border-box-control/border-box-control-split-controls/component.js +1 -1
- package/build-module/border-box-control/border-box-control-split-controls/component.js.map +1 -1
- package/build-module/border-control/border-control/component.js +2 -0
- package/build-module/border-control/border-control/component.js.map +1 -1
- package/build-module/box-control/all-input-control.js +1 -0
- package/build-module/box-control/all-input-control.js.map +1 -1
- package/build-module/box-control/axial-input-controls.js +1 -0
- package/build-module/box-control/axial-input-controls.js.map +1 -1
- package/build-module/box-control/index.js +22 -15
- package/build-module/box-control/index.js.map +1 -1
- package/build-module/box-control/input-controls.js +1 -0
- package/build-module/box-control/input-controls.js.map +1 -1
- package/build-module/box-control/types.js.map +1 -1
- package/build-module/circular-option-picker/circular-option-picker-actions.js +1 -0
- package/build-module/circular-option-picker/circular-option-picker-actions.js.map +1 -1
- package/build-module/circular-option-picker/circular-option-picker-option.js +1 -0
- package/build-module/circular-option-picker/circular-option-picker-option.js.map +1 -1
- package/build-module/clipboard-button/index.js +5 -3
- package/build-module/clipboard-button/index.js.map +1 -1
- package/build-module/color-palette/index.native.js +0 -1
- package/build-module/color-palette/index.native.js.map +1 -1
- package/build-module/color-picker/input-with-slider.js +2 -2
- package/build-module/color-picker/input-with-slider.js.map +1 -1
- package/build-module/composite/item.js +0 -9
- package/build-module/composite/item.js.map +1 -1
- package/build-module/context/context-connect.js +0 -1
- package/build-module/context/context-connect.js.map +1 -1
- package/build-module/custom-gradient-picker/gradient-bar/control-points.js +2 -0
- package/build-module/custom-gradient-picker/gradient-bar/control-points.js.map +1 -1
- package/build-module/custom-gradient-picker/index.js +2 -0
- package/build-module/custom-gradient-picker/index.js.map +1 -1
- package/build-module/custom-gradient-picker/types.js.map +1 -1
- package/build-module/custom-select-control/index.js +8 -0
- package/build-module/custom-select-control/index.js.map +1 -1
- package/build-module/custom-select-control/types.js.map +1 -1
- package/build-module/drop-zone/index.js +19 -13
- package/build-module/drop-zone/index.js.map +1 -1
- package/build-module/drop-zone/types.js.map +1 -1
- package/build-module/dropdown-menu/index.js +1 -0
- package/build-module/dropdown-menu/index.js.map +1 -1
- package/build-module/duotone-picker/color-list-picker/index.js +10 -15
- package/build-module/duotone-picker/color-list-picker/index.js.map +1 -1
- package/build-module/external-link/index.js +0 -1
- package/build-module/external-link/index.js.map +1 -1
- package/build-module/font-size-picker/font-size-picker-select.js +1 -0
- package/build-module/font-size-picker/font-size-picker-select.js.map +1 -1
- package/build-module/font-size-picker/index.js +1 -0
- package/build-module/font-size-picker/index.js.map +1 -1
- package/build-module/form-file-upload/index.js +13 -2
- package/build-module/form-file-upload/index.js.map +1 -1
- package/build-module/form-file-upload/types.js.map +1 -1
- package/build-module/form-token-field/index.js +6 -1
- package/build-module/form-token-field/index.js.map +1 -1
- package/build-module/gradient-picker/index.js +2 -0
- package/build-module/gradient-picker/index.js.map +1 -1
- package/build-module/gradient-picker/types.js.map +1 -1
- package/build-module/icon/index.js +9 -0
- package/build-module/icon/index.js.map +1 -1
- package/build-module/index.js +1 -1
- package/build-module/index.js.map +1 -1
- package/build-module/menu/checkbox-item.js +5 -9
- package/build-module/menu/checkbox-item.js.map +1 -1
- package/build-module/menu/group-label.js +4 -1
- package/build-module/menu/group-label.js.map +1 -1
- package/build-module/menu/group.js +4 -1
- package/build-module/menu/group.js.map +1 -1
- package/build-module/menu/item-help-text.js +6 -1
- package/build-module/menu/item-help-text.js.map +1 -1
- package/build-module/menu/item-label.js +6 -1
- package/build-module/menu/item-label.js.map +1 -1
- package/build-module/menu/item.js +4 -8
- package/build-module/menu/item.js.map +1 -1
- package/build-module/menu/radio-item.js +5 -9
- package/build-module/menu/radio-item.js.map +1 -1
- package/build-module/menu/separator.js +5 -2
- package/build-module/menu/separator.js.map +1 -1
- package/build-module/modal/aria-helper.js +0 -1
- package/build-module/modal/aria-helper.js.map +1 -1
- package/build-module/modal/index.js +0 -1
- package/build-module/modal/index.js.map +1 -1
- package/build-module/number-control/index.js +8 -0
- package/build-module/number-control/index.js.map +1 -1
- package/build-module/number-control/types.js.map +1 -1
- package/build-module/range-control/index.js +2 -1
- package/build-module/range-control/index.js.map +1 -1
- package/build-module/range-control/mark.js +0 -1
- package/build-module/range-control/mark.js.map +1 -1
- package/build-module/range-control/styles/range-control-styles.js +33 -45
- package/build-module/range-control/styles/range-control-styles.js.map +1 -1
- package/build-module/resizable-box/index.js +9 -1
- package/build-module/resizable-box/index.js.map +1 -1
- package/build-module/slot-fill/bubbles-virtually/fill.js +9 -13
- package/build-module/slot-fill/bubbles-virtually/fill.js.map +1 -1
- package/build-module/slot-fill/bubbles-virtually/slot-fill-provider.js +6 -10
- package/build-module/slot-fill/bubbles-virtually/slot-fill-provider.js.map +1 -1
- package/build-module/slot-fill/bubbles-virtually/slot.js +4 -10
- package/build-module/slot-fill/bubbles-virtually/slot.js.map +1 -1
- package/build-module/slot-fill/types.js.map +1 -1
- package/build-module/style-provider/index.js +0 -1
- package/build-module/style-provider/index.js.map +1 -1
- package/build-module/tabs/tab.js +0 -15
- package/build-module/tabs/tab.js.map +1 -1
- package/build-module/toolbar/toolbar-button/index.js +2 -0
- package/build-module/toolbar/toolbar-button/index.js.map +1 -1
- package/build-module/tools-panel/tools-panel/component.js +2 -0
- package/build-module/tools-panel/tools-panel/component.js.map +1 -1
- package/build-module/tree-grid/cell.js +4 -1
- package/build-module/tree-grid/cell.js.map +1 -1
- package/build-module/tree-grid/types.js.map +1 -1
- package/build-module/unit-control/index.js +10 -1
- package/build-module/unit-control/index.js.map +1 -1
- package/build-module/unit-control/types.js.map +1 -1
- package/build-style/style-rtl.css +15 -11
- package/build-style/style.css +15 -11
- package/build-types/alignment-matrix-control/styles.d.ts.map +1 -1
- package/build-types/angle-picker-control/styles/angle-picker-control-styles.d.ts.map +1 -1
- package/build-types/animation/index.d.ts +1 -1
- package/build-types/animation/index.d.ts.map +1 -1
- package/build-types/base-control/hooks.d.ts +4 -4
- package/build-types/base-control/styles/base-control-styles.d.ts.map +1 -1
- package/build-types/border-box-control/border-box-control/hook.d.ts +83 -83
- package/build-types/border-box-control/border-box-control-linked-button/hook.d.ts +93 -93
- package/build-types/border-box-control/border-box-control-split-controls/hook.d.ts +83 -83
- package/build-types/border-box-control/border-box-control-visualizer/hook.d.ts +83 -83
- package/build-types/border-control/border-control/component.d.ts.map +1 -1
- package/build-types/border-control/border-control/hook.d.ts +83 -83
- package/build-types/border-control/border-control-dropdown/hook.d.ts +83 -83
- package/build-types/border-control/stories/index.story.d.ts.map +1 -1
- package/build-types/box-control/all-input-control.d.ts.map +1 -1
- package/build-types/box-control/axial-input-controls.d.ts.map +1 -1
- package/build-types/box-control/index.d.ts +14 -13
- package/build-types/box-control/index.d.ts.map +1 -1
- package/build-types/box-control/input-controls.d.ts.map +1 -1
- package/build-types/box-control/stories/index.story.d.ts +852 -816
- package/build-types/box-control/stories/index.story.d.ts.map +1 -1
- package/build-types/box-control/styles/box-control-icon-styles.d.ts.map +1 -1
- package/build-types/box-control/styles/box-control-styles.d.ts +3 -2
- package/build-types/box-control/styles/box-control-styles.d.ts.map +1 -1
- package/build-types/box-control/types.d.ts +16 -2
- package/build-types/box-control/types.d.ts.map +1 -1
- package/build-types/card/card/hook.d.ts +83 -83
- package/build-types/card/card-body/hook.d.ts +83 -83
- package/build-types/card/card-divider/hook.d.ts +84 -84
- package/build-types/card/card-footer/hook.d.ts +83 -83
- package/build-types/card/card-header/hook.d.ts +83 -83
- package/build-types/card/card-media/hook.d.ts +83 -83
- package/build-types/circular-option-picker/circular-option-picker-actions.d.ts.map +1 -1
- package/build-types/circular-option-picker/circular-option-picker-option.d.ts.map +1 -1
- package/build-types/clipboard-button/index.d.ts.map +1 -1
- package/build-types/color-picker/styles.d.ts +3 -2
- package/build-types/color-picker/styles.d.ts.map +1 -1
- package/build-types/combobox-control/stories/index.story.d.ts.map +1 -1
- package/build-types/composite/index.d.ts.map +1 -1
- package/build-types/composite/item.d.ts.map +1 -1
- package/build-types/context/constants.d.ts.map +1 -1
- package/build-types/context/get-styled-class-name-from-key.d.ts +1 -9
- package/build-types/context/get-styled-class-name-from-key.d.ts.map +1 -1
- package/build-types/custom-gradient-picker/gradient-bar/control-points.d.ts.map +1 -1
- package/build-types/custom-gradient-picker/index.d.ts +1 -1
- package/build-types/custom-gradient-picker/index.d.ts.map +1 -1
- package/build-types/custom-gradient-picker/types.d.ts +6 -0
- package/build-types/custom-gradient-picker/types.d.ts.map +1 -1
- package/build-types/custom-select-control/index.d.ts.map +1 -1
- package/build-types/custom-select-control/stories/index.story.d.ts.map +1 -1
- package/build-types/custom-select-control/types.d.ts +7 -0
- package/build-types/custom-select-control/types.d.ts.map +1 -1
- package/build-types/custom-select-control-v2/stories/index.story.d.ts.map +1 -1
- package/build-types/custom-select-control-v2/styles.d.ts.map +1 -1
- package/build-types/date-time/date/styles.d.ts.map +1 -1
- package/build-types/date-time/stories/date-time.story.d.ts.map +1 -1
- package/build-types/date-time/stories/date.story.d.ts.map +1 -1
- package/build-types/date-time/stories/time.story.d.ts.map +1 -1
- package/build-types/date-time/time/styles.d.ts +8 -4
- package/build-types/date-time/time/styles.d.ts.map +1 -1
- package/build-types/dimension-control/stories/index.story.d.ts.map +1 -1
- package/build-types/drop-zone/index.d.ts +1 -1
- package/build-types/drop-zone/index.d.ts.map +1 -1
- package/build-types/drop-zone/types.d.ts +5 -0
- package/build-types/drop-zone/types.d.ts.map +1 -1
- package/build-types/dropdown-menu/index.d.ts.map +1 -1
- package/build-types/duotone-picker/color-list-picker/index.d.ts.map +1 -1
- package/build-types/elevation/hook.d.ts +83 -83
- package/build-types/flex/flex/hook.d.ts +83 -83
- package/build-types/flex/flex-block/hook.d.ts +83 -83
- package/build-types/flex/flex-item/hook.d.ts +83 -83
- package/build-types/focal-point-picker/stories/index.story.d.ts.map +1 -1
- package/build-types/focal-point-picker/styles/focal-point-picker-style.d.ts +2 -1
- package/build-types/focal-point-picker/styles/focal-point-picker-style.d.ts.map +1 -1
- package/build-types/font-size-picker/font-size-picker-select.d.ts.map +1 -1
- package/build-types/font-size-picker/index.d.ts.map +1 -1
- package/build-types/font-size-picker/styles.d.ts.map +1 -1
- package/build-types/form-file-upload/index.d.ts +2 -1
- package/build-types/form-file-upload/index.d.ts.map +1 -1
- package/build-types/form-file-upload/stories/index.story.d.ts.map +1 -1
- package/build-types/form-file-upload/types.d.ts +10 -8
- package/build-types/form-file-upload/types.d.ts.map +1 -1
- package/build-types/form-token-field/index.d.ts.map +1 -1
- package/build-types/form-token-field/stories/index.story.d.ts.map +1 -1
- package/build-types/gradient-picker/index.d.ts +1 -1
- package/build-types/gradient-picker/index.d.ts.map +1 -1
- package/build-types/gradient-picker/types.d.ts +6 -0
- package/build-types/gradient-picker/types.d.ts.map +1 -1
- package/build-types/grid/hook.d.ts +83 -83
- package/build-types/h-stack/hook.d.ts +83 -83
- package/build-types/heading/hook.d.ts +82 -82
- package/build-types/higher-order/with-fallback-styles/index.d.ts +2 -2
- package/build-types/higher-order/with-filters/index.d.ts +4 -4
- package/build-types/icon/index.d.ts +23 -7
- package/build-types/icon/index.d.ts.map +1 -1
- package/build-types/icon/stories/index.story.d.ts +7 -1
- package/build-types/icon/stories/index.story.d.ts.map +1 -1
- package/build-types/index.d.ts +1 -1
- package/build-types/index.d.ts.map +1 -1
- package/build-types/input-control/styles/input-control-styles.d.ts.map +1 -1
- package/build-types/item-group/item/hook.d.ts +83 -83
- package/build-types/item-group/item-group/hook.d.ts +83 -83
- package/build-types/lock-unlock.d.ts +2 -2
- package/build-types/menu/checkbox-item.d.ts.map +1 -1
- package/build-types/menu/group-label.d.ts.map +1 -1
- package/build-types/menu/group.d.ts.map +1 -1
- package/build-types/menu/index.d.ts.map +1 -1
- package/build-types/menu/item-help-text.d.ts.map +1 -1
- package/build-types/menu/item-label.d.ts.map +1 -1
- package/build-types/menu/item.d.ts.map +1 -1
- package/build-types/menu/radio-item.d.ts.map +1 -1
- package/build-types/menu/separator.d.ts.map +1 -1
- package/build-types/menu/styles.d.ts.map +1 -1
- package/build-types/menu-item/stories/index.story.d.ts.map +1 -1
- package/build-types/navigation/styles/navigation-styles.d.ts.map +1 -1
- package/build-types/navigator/navigator-back-button/hook.d.ts +92 -92
- package/build-types/navigator/navigator-button/hook.d.ts +92 -92
- package/build-types/number-control/index.d.ts +2 -1
- package/build-types/number-control/index.d.ts.map +1 -1
- package/build-types/number-control/stories/index.story.d.ts +2 -1
- package/build-types/number-control/stories/index.story.d.ts.map +1 -1
- package/build-types/number-control/types.d.ts +7 -0
- package/build-types/number-control/types.d.ts.map +1 -1
- package/build-types/popover/stories/index.story.d.ts.map +1 -1
- package/build-types/progress-bar/styles.d.ts.map +1 -1
- package/build-types/range-control/index.d.ts +1 -1
- package/build-types/range-control/index.d.ts.map +1 -1
- package/build-types/range-control/mark.d.ts.map +1 -1
- package/build-types/range-control/styles/range-control-styles.d.ts +4 -3
- package/build-types/range-control/styles/range-control-styles.d.ts.map +1 -1
- package/build-types/resizable-box/index.d.ts.map +1 -1
- package/build-types/resizable-box/resize-tooltip/styles/resize-tooltip.styles.d.ts.map +1 -1
- package/build-types/resizable-box/stories/index.story.d.ts.map +1 -1
- package/build-types/scrollable/hook.d.ts +83 -83
- package/build-types/scrollable/stories/index.story.d.ts.map +1 -1
- package/build-types/select-control/index.d.ts.map +1 -1
- package/build-types/select-control/stories/index.story.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.map +1 -1
- package/build-types/slot-fill/bubbles-virtually/slot-fill-provider.d.ts.map +1 -1
- package/build-types/slot-fill/bubbles-virtually/slot.d.ts.map +1 -1
- package/build-types/slot-fill/bubbles-virtually/use-slot-fills.d.ts +1 -1
- package/build-types/slot-fill/types.d.ts +4 -3
- package/build-types/slot-fill/types.d.ts.map +1 -1
- package/build-types/spacer/hook.d.ts +83 -83
- package/build-types/spinner/styles.d.ts.map +1 -1
- package/build-types/surface/hook.d.ts +83 -83
- package/build-types/tabs/tab.d.ts +3 -0
- package/build-types/tabs/tab.d.ts.map +1 -1
- package/build-types/text/hook.d.ts +83 -83
- package/build-types/toggle-group-control/toggle-group-control-option-base/styles.d.ts.map +1 -1
- package/build-types/toolbar/toolbar-button/index.d.ts.map +1 -1
- package/build-types/tools-panel/stories/index.story.d.ts.map +1 -1
- package/build-types/tools-panel/tools-panel/component.d.ts +2 -0
- package/build-types/tools-panel/tools-panel/component.d.ts.map +1 -1
- package/build-types/tools-panel/tools-panel/hook.d.ts +83 -83
- package/build-types/tools-panel/tools-panel-header/hook.d.ts +83 -83
- package/build-types/tools-panel/tools-panel-item/hook.d.ts +83 -83
- package/build-types/tree-grid/cell.d.ts.map +1 -1
- package/build-types/tree-grid/types.d.ts +1 -1
- package/build-types/tree-grid/types.d.ts.map +1 -1
- package/build-types/truncate/hook.d.ts +83 -83
- package/build-types/unit-control/index.d.ts +3 -2
- package/build-types/unit-control/index.d.ts.map +1 -1
- package/build-types/unit-control/stories/index.story.d.ts.map +1 -1
- package/build-types/unit-control/styles/unit-control-styles.d.ts +2 -1
- package/build-types/unit-control/styles/unit-control-styles.d.ts.map +1 -1
- package/build-types/unit-control/types.d.ts +7 -0
- package/build-types/unit-control/types.d.ts.map +1 -1
- package/build-types/utils/hooks/use-controlled-state.d.ts +1 -1
- package/build-types/utils/hooks/use-controlled-state.d.ts.map +1 -1
- package/build-types/utils/rtl.d.ts +1 -1
- package/build-types/utils/rtl.d.ts.map +1 -1
- package/build-types/v-stack/hook.d.ts +83 -83
- package/build-types/z-stack/styles.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/alignment-matrix-control/stories/index.story.tsx +2 -2
- package/src/angle-picker-control/index.tsx +1 -1
- package/src/angle-picker-control/stories/index.story.tsx +2 -2
- package/src/animation/index.tsx +0 -1
- package/src/base-control/stories/index.story.tsx +1 -1
- package/src/border-box-control/border-box-control-split-controls/component.tsx +1 -1
- package/src/border-box-control/stories/index.story.tsx +1 -1
- package/src/border-control/border-control/component.tsx +2 -0
- package/src/border-control/stories/index.story.tsx +1 -1
- package/src/box-control/README.md +80 -60
- package/src/box-control/all-input-control.tsx +1 -0
- package/src/box-control/axial-input-controls.tsx +1 -0
- package/src/box-control/docs-manifest.json +5 -0
- package/src/box-control/index.tsx +23 -15
- package/src/box-control/input-controls.tsx +1 -0
- package/src/box-control/stories/index.story.tsx +2 -1
- package/src/box-control/test/index.tsx +33 -26
- package/src/box-control/types.ts +71 -54
- package/src/button-group/stories/index.story.tsx +1 -1
- package/src/card/stories/index.story.tsx +2 -2
- package/src/checkbox-control/stories/index.story.tsx +1 -1
- package/src/circular-option-picker/circular-option-picker-actions.tsx +1 -0
- package/src/circular-option-picker/circular-option-picker-option.tsx +1 -0
- package/src/circular-option-picker/stories/index.story.tsx +2 -2
- package/src/circular-option-picker/style.scss +2 -2
- package/src/clipboard-button/index.tsx +5 -3
- package/src/color-palette/stories/index.story.tsx +3 -3
- package/src/color-picker/input-with-slider.tsx +1 -1
- package/src/color-picker/stories/index.story.tsx +2 -2
- package/src/combobox-control/stories/index.story.tsx +1 -1
- package/src/composite/item.tsx +1 -19
- package/src/composite/stories/index.story.tsx +3 -3
- package/src/confirm-dialog/stories/index.story.tsx +1 -1
- package/src/custom-gradient-picker/gradient-bar/control-points.tsx +2 -0
- package/src/custom-gradient-picker/index.tsx +2 -0
- package/src/custom-gradient-picker/style.scss +1 -1
- package/src/custom-gradient-picker/types.ts +6 -0
- package/src/custom-select-control/README.md +2 -0
- package/src/custom-select-control/index.tsx +9 -0
- package/src/custom-select-control/stories/index.story.tsx +3 -2
- package/src/custom-select-control/test/index.tsx +13 -9
- package/src/custom-select-control/types.ts +7 -0
- package/src/custom-select-control-v2/stories/index.story.tsx +2 -2
- package/src/date-time/stories/date-time.story.tsx +4 -1
- package/src/date-time/stories/date.story.tsx +4 -1
- package/src/date-time/stories/time.story.tsx +4 -1
- package/src/dimension-control/stories/index.story.tsx +1 -1
- package/src/disabled/stories/index.story.tsx +3 -3
- package/src/divider/stories/index.story.tsx +1 -1
- package/src/draggable/stories/index.story.tsx +2 -2
- package/src/drop-zone/index.tsx +21 -24
- package/src/drop-zone/types.ts +5 -0
- package/src/dropdown/stories/index.story.tsx +7 -7
- package/src/dropdown-menu/index.tsx +4 -1
- package/src/dropdown-menu/stories/index.story.tsx +3 -3
- package/src/dropdown-menu/style.scss +1 -1
- package/src/duotone-picker/color-list-picker/index.tsx +8 -8
- package/src/duotone-picker/color-list-picker/style.scss +0 -6
- package/src/duotone-picker/stories/duotone-picker.story.tsx +1 -1
- package/src/flex/stories/index.story.tsx +1 -1
- package/src/font-size-picker/font-size-picker-select.tsx +1 -0
- package/src/font-size-picker/index.tsx +1 -0
- package/src/font-size-picker/stories/index.story.tsx +1 -1
- package/src/form-file-upload/README.md +58 -48
- package/src/form-file-upload/docs-manifest.json +5 -0
- package/src/form-file-upload/index.tsx +12 -1
- package/src/form-file-upload/stories/index.story.tsx +4 -3
- package/src/form-file-upload/test/index.tsx +5 -1
- package/src/form-file-upload/types.ts +10 -8
- package/src/form-token-field/README.md +1 -0
- package/src/form-token-field/index.tsx +7 -0
- package/src/form-token-field/stories/index.story.tsx +4 -2
- package/src/form-token-field/test/index.tsx +5 -1
- package/src/gradient-picker/README.md +8 -0
- package/src/gradient-picker/index.tsx +2 -0
- package/src/gradient-picker/stories/index.story.tsx +1 -1
- package/src/gradient-picker/types.ts +6 -0
- package/src/grid/stories/index.story.tsx +1 -1
- package/src/h-stack/stories/index.story.tsx +2 -2
- package/src/icon/README.md +22 -65
- package/src/icon/docs-manifest.json +5 -0
- package/src/icon/index.tsx +28 -13
- package/src/icon/stories/index.story.tsx +50 -8
- package/src/index.ts +1 -5
- package/src/input-control/stories/index.story.tsx +4 -4
- package/src/item-group/stories/index.story.tsx +2 -2
- package/src/menu/checkbox-item.tsx +9 -7
- package/src/menu/group-label.tsx +8 -1
- package/src/menu/group.tsx +8 -1
- package/src/menu/item-help-text.tsx +10 -1
- package/src/menu/item-label.tsx +10 -1
- package/src/menu/item.tsx +8 -6
- package/src/menu/radio-item.tsx +9 -7
- package/src/menu/separator.tsx +9 -2
- package/src/menu/stories/index.story.tsx +2 -2
- package/src/menu-group/stories/index.story.tsx +1 -1
- package/src/menu-item/stories/index.story.tsx +1 -1
- package/src/menu-items-choice/stories/index.story.tsx +1 -1
- package/src/menu-items-choice/style.scss +1 -0
- package/src/modal/stories/index.story.tsx +2 -2
- package/src/modal/test/index.tsx +2 -1
- package/src/navigable-container/stories/navigable-menu.story.tsx +1 -1
- package/src/navigable-container/stories/tabbable-container.story.tsx +1 -1
- package/src/navigation/stories/index.story.tsx +4 -4
- package/src/navigator/stories/index.story.tsx +3 -3
- package/src/number-control/README.md +2 -1
- package/src/number-control/index.tsx +9 -0
- package/src/number-control/stories/index.story.tsx +2 -1
- package/src/number-control/test/index.tsx +5 -1
- package/src/number-control/types.ts +7 -0
- package/src/panel/stories/index.story.tsx +1 -1
- package/src/placeholder/stories/index.story.tsx +3 -3
- package/src/popover/stories/index.story.tsx +11 -9
- package/src/query-controls/stories/index.story.tsx +6 -6
- package/src/radio-control/stories/index.story.tsx +1 -1
- package/src/radio-group/stories/index.story.tsx +3 -3
- package/src/range-control/index.tsx +1 -0
- package/src/range-control/mark.tsx +0 -1
- package/src/range-control/stories/index.story.tsx +7 -7
- package/src/range-control/styles/range-control-styles.ts +18 -19
- package/src/resizable-box/index.tsx +10 -0
- package/src/resizable-box/stories/index.story.tsx +1 -1
- package/src/resizable-box/style.scss +8 -0
- package/src/responsive-wrapper/stories/index.story.tsx +1 -1
- package/src/sandbox/stories/index.story.tsx +1 -1
- package/src/scrollable/stories/index.story.tsx +2 -1
- package/src/search-control/stories/index.story.tsx +1 -1
- package/src/select-control/stories/index.story.tsx +1 -1
- package/src/slot-fill/bubbles-virtually/fill.tsx +7 -11
- package/src/slot-fill/bubbles-virtually/slot-fill-provider.tsx +2 -13
- package/src/slot-fill/bubbles-virtually/slot.tsx +4 -7
- package/src/slot-fill/stories/index.story.tsx +2 -2
- package/src/slot-fill/types.ts +4 -3
- package/src/snackbar/stories/index.story.tsx +4 -4
- package/src/snackbar/stories/list.story.tsx +2 -2
- package/src/surface/stories/index.story.tsx +1 -1
- package/src/tabs/tab.tsx +0 -18
- package/src/tabs/test/index.tsx +1492 -947
- package/src/text-control/stories/index.story.tsx +1 -1
- package/src/textarea-control/stories/index.story.tsx +1 -1
- package/src/theme/stories/index.story.tsx +1 -1
- package/src/toggle-control/stories/index.story.tsx +1 -1
- package/src/toggle-group-control/stories/index.story.tsx +1 -1
- package/src/toolbar/stories/index.story.tsx +1 -1
- package/src/toolbar/toolbar-button/index.tsx +2 -0
- package/src/tools-panel/stories/index.story.tsx +15 -3
- package/src/tools-panel/test/index.tsx +0 -17
- package/src/tools-panel/tools-panel/README.md +4 -0
- package/src/tools-panel/tools-panel/component.tsx +2 -0
- package/src/tooltip/stories/index.story.tsx +1 -1
- package/src/tree-grid/cell.tsx +5 -1
- package/src/tree-grid/stories/index.story.tsx +1 -1
- package/src/tree-grid/types.ts +1 -1
- package/src/tree-select/stories/index.story.tsx +1 -1
- package/src/unit-control/README.md +3 -3
- package/src/unit-control/index.tsx +11 -1
- package/src/unit-control/stories/index.story.tsx +5 -4
- package/src/unit-control/test/index.tsx +5 -1
- package/src/unit-control/types.ts +7 -0
- package/src/view/stories/index.story.tsx +1 -1
- package/src/visually-hidden/stories/index.story.tsx +1 -1
- package/src/z-stack/stories/index.story.tsx +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/build/menu/use-temporary-focus-visible-fix.js +0 -27
- package/build/menu/use-temporary-focus-visible-fix.js.map +0 -1
- package/build-module/menu/use-temporary-focus-visible-fix.js +0 -20
- package/build-module/menu/use-temporary-focus-visible-fix.js.map +0 -1
- package/build-types/menu/use-temporary-focus-visible-fix.d.ts +0 -8
- package/build-types/menu/use-temporary-focus-visible-fix.d.ts.map +0 -1
- package/src/menu/use-temporary-focus-visible-fix.ts +0 -22
package/src/tabs/test/index.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import { render } from '@ariakit/test/react';
|
|
|
9
9
|
* WordPress dependencies
|
|
10
10
|
*/
|
|
11
11
|
import { useEffect, useState } from '@wordpress/element';
|
|
12
|
+
import { isRTL } from '@wordpress/i18n';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Internal dependencies
|
|
@@ -16,6 +17,16 @@ import { useEffect, useState } from '@wordpress/element';
|
|
|
16
17
|
import { Tabs } from '..';
|
|
17
18
|
import type { TabsProps } from '../types';
|
|
18
19
|
|
|
20
|
+
// Setup mocking the `isRTL` function to test arrow key navigation behavior.
|
|
21
|
+
jest.mock( '@wordpress/i18n', () => {
|
|
22
|
+
const original = jest.requireActual( '@wordpress/i18n' );
|
|
23
|
+
return {
|
|
24
|
+
...original,
|
|
25
|
+
isRTL: jest.fn( () => false ),
|
|
26
|
+
};
|
|
27
|
+
} );
|
|
28
|
+
const mockedIsRTL = isRTL as jest.Mock;
|
|
29
|
+
|
|
19
30
|
type Tab = {
|
|
20
31
|
tabId: string;
|
|
21
32
|
title: string;
|
|
@@ -50,6 +61,30 @@ const TABS: Tab[] = [
|
|
|
50
61
|
},
|
|
51
62
|
];
|
|
52
63
|
|
|
64
|
+
const TABS_WITH_ALPHA_DISABLED = TABS.map( ( tabObj ) =>
|
|
65
|
+
tabObj.tabId === 'alpha'
|
|
66
|
+
? {
|
|
67
|
+
...tabObj,
|
|
68
|
+
tab: {
|
|
69
|
+
...tabObj.tab,
|
|
70
|
+
disabled: true,
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
: tabObj
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const TABS_WITH_BETA_DISABLED = TABS.map( ( tabObj ) =>
|
|
77
|
+
tabObj.tabId === 'beta'
|
|
78
|
+
? {
|
|
79
|
+
...tabObj,
|
|
80
|
+
tab: {
|
|
81
|
+
...tabObj.tab,
|
|
82
|
+
disabled: true,
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
: tabObj
|
|
86
|
+
);
|
|
87
|
+
|
|
53
88
|
const TABS_WITH_DELTA: Tab[] = [
|
|
54
89
|
...TABS,
|
|
55
90
|
{
|
|
@@ -141,11 +176,47 @@ const ControlledTabs = ( {
|
|
|
141
176
|
);
|
|
142
177
|
};
|
|
143
178
|
|
|
144
|
-
const getSelectedTab = async () =>
|
|
145
|
-
await screen.findByRole( 'tab', { selected: true } );
|
|
146
|
-
|
|
147
179
|
let originalGetClientRects: () => DOMRectList;
|
|
148
180
|
|
|
181
|
+
async function waitForComponentToBeInitializedWithSelectedTab(
|
|
182
|
+
selectedTabName: string | undefined
|
|
183
|
+
) {
|
|
184
|
+
if ( ! selectedTabName ) {
|
|
185
|
+
// Wait for the tablist to be tabbable as a mean to know
|
|
186
|
+
// that ariakit has finished initializing.
|
|
187
|
+
await waitFor( () =>
|
|
188
|
+
expect( screen.getByRole( 'tablist' ) ).toHaveAttribute(
|
|
189
|
+
'tabindex',
|
|
190
|
+
expect.stringMatching( /^(0|-1)$/ )
|
|
191
|
+
)
|
|
192
|
+
);
|
|
193
|
+
// No initially selected tabs or tabpanels.
|
|
194
|
+
await waitFor( () =>
|
|
195
|
+
expect(
|
|
196
|
+
screen.queryByRole( 'tab', { selected: true } )
|
|
197
|
+
).not.toBeInTheDocument()
|
|
198
|
+
);
|
|
199
|
+
await waitFor( () =>
|
|
200
|
+
expect( screen.queryByRole( 'tabpanel' ) ).not.toBeInTheDocument()
|
|
201
|
+
);
|
|
202
|
+
} else {
|
|
203
|
+
// Waiting for a tab to be selected is a sign that the component
|
|
204
|
+
// has fully initialized.
|
|
205
|
+
expect(
|
|
206
|
+
await screen.findByRole( 'tab', {
|
|
207
|
+
selected: true,
|
|
208
|
+
name: selectedTabName,
|
|
209
|
+
} )
|
|
210
|
+
).toBeVisible();
|
|
211
|
+
// The corresponding tabpanel is also shown.
|
|
212
|
+
expect(
|
|
213
|
+
screen.getByRole( 'tabpanel', {
|
|
214
|
+
name: selectedTabName,
|
|
215
|
+
} )
|
|
216
|
+
).toBeVisible();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
149
220
|
describe( 'Tabs', () => {
|
|
150
221
|
beforeAll( () => {
|
|
151
222
|
originalGetClientRects = window.HTMLElement.prototype.getClientRects;
|
|
@@ -162,13 +233,16 @@ describe( 'Tabs', () => {
|
|
|
162
233
|
window.HTMLElement.prototype.getClientRects = originalGetClientRects;
|
|
163
234
|
} );
|
|
164
235
|
|
|
165
|
-
describe( '
|
|
166
|
-
it( 'should
|
|
236
|
+
describe( 'Adherence to spec and basic behavior', () => {
|
|
237
|
+
it( 'should apply the correct roles, semantics and attributes', async () => {
|
|
167
238
|
await render( <UncontrolledTabs tabs={ TABS } /> );
|
|
168
239
|
|
|
240
|
+
// Alpha is automatically selected as the selected tab.
|
|
241
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
242
|
+
|
|
169
243
|
const tabList = screen.getByRole( 'tablist' );
|
|
170
244
|
const allTabs = screen.getAllByRole( 'tab' );
|
|
171
|
-
const
|
|
245
|
+
const allTabpanels = screen.getAllByRole( 'tabpanel' );
|
|
172
246
|
|
|
173
247
|
expect( tabList ).toBeVisible();
|
|
174
248
|
expect( tabList ).toHaveAttribute(
|
|
@@ -178,133 +252,103 @@ describe( 'Tabs', () => {
|
|
|
178
252
|
|
|
179
253
|
expect( allTabs ).toHaveLength( TABS.length );
|
|
180
254
|
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
|
|
255
|
+
// Only 1 tab panel is accessible — the one associated with the
|
|
256
|
+
// selected tab. The selected `tab` aria-controls the active
|
|
257
|
+
/// `tabpanel`, which is `aria-labelledby` the selected `tab`.
|
|
258
|
+
expect( allTabpanels ).toHaveLength( 1 );
|
|
259
|
+
|
|
260
|
+
expect( allTabpanels[ 0 ] ).toBeVisible();
|
|
184
261
|
expect( allTabs[ 0 ] ).toHaveAttribute(
|
|
185
262
|
'aria-controls',
|
|
186
|
-
|
|
263
|
+
allTabpanels[ 0 ].getAttribute( 'id' )
|
|
187
264
|
);
|
|
188
|
-
expect(
|
|
265
|
+
expect( allTabpanels[ 0 ] ).toHaveAttribute(
|
|
189
266
|
'aria-labelledby',
|
|
190
267
|
allTabs[ 0 ].getAttribute( 'id' )
|
|
191
268
|
);
|
|
192
269
|
} );
|
|
193
|
-
} );
|
|
194
|
-
describe( 'Focus Behavior', () => {
|
|
195
|
-
it( 'should focus on the related TabPanel when pressing the Tab key', async () => {
|
|
196
|
-
await render( <UncontrolledTabs tabs={ TABS } /> );
|
|
197
270
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const selectedTabPanel = await screen.findByRole( 'tabpanel' );
|
|
201
|
-
|
|
202
|
-
// Tab should initially focus the first tab in the tablist, which
|
|
203
|
-
// is Alpha.
|
|
204
|
-
await press.Tab();
|
|
205
|
-
expect(
|
|
206
|
-
await screen.findByRole( 'tab', { name: 'Alpha' } )
|
|
207
|
-
).toHaveFocus();
|
|
208
|
-
|
|
209
|
-
// By default the tabpanel should receive focus
|
|
210
|
-
await press.Tab();
|
|
211
|
-
expect( selectedTabPanel ).toHaveFocus();
|
|
212
|
-
} );
|
|
213
|
-
it( 'should not focus on the related TabPanel when pressing the Tab key if `focusable: false` is set', async () => {
|
|
214
|
-
const TABS_WITH_ALPHA_FOCUSABLE_FALSE = TABS.map( ( tabObj ) =>
|
|
215
|
-
tabObj.tabId === 'alpha'
|
|
216
|
-
? {
|
|
217
|
-
...tabObj,
|
|
218
|
-
content: (
|
|
219
|
-
<>
|
|
220
|
-
Selected Tab: Alpha
|
|
221
|
-
<button>Alpha Button</button>
|
|
222
|
-
</>
|
|
223
|
-
),
|
|
224
|
-
tabpanel: { focusable: false },
|
|
225
|
-
}
|
|
226
|
-
: tabObj
|
|
227
|
-
);
|
|
271
|
+
it( 'should associate each `tab` with the correct `tabpanel`, even if they are not rendered in the same order', async () => {
|
|
272
|
+
const TABS_WITH_DELTA_REVERSED = [ ...TABS_WITH_DELTA ].reverse();
|
|
228
273
|
|
|
229
274
|
await render(
|
|
230
|
-
<
|
|
275
|
+
<Tabs>
|
|
276
|
+
<Tabs.TabList>
|
|
277
|
+
{ TABS_WITH_DELTA.map( ( tabObj ) => (
|
|
278
|
+
<Tabs.Tab
|
|
279
|
+
key={ tabObj.tabId }
|
|
280
|
+
tabId={ tabObj.tabId }
|
|
281
|
+
className={ tabObj.tab.className }
|
|
282
|
+
disabled={ tabObj.tab.disabled }
|
|
283
|
+
>
|
|
284
|
+
{ tabObj.title }
|
|
285
|
+
</Tabs.Tab>
|
|
286
|
+
) ) }
|
|
287
|
+
</Tabs.TabList>
|
|
288
|
+
{ TABS_WITH_DELTA_REVERSED.map( ( tabObj ) => (
|
|
289
|
+
<Tabs.TabPanel
|
|
290
|
+
key={ tabObj.tabId }
|
|
291
|
+
tabId={ tabObj.tabId }
|
|
292
|
+
focusable={ tabObj.tabpanel?.focusable }
|
|
293
|
+
>
|
|
294
|
+
{ tabObj.content }
|
|
295
|
+
</Tabs.TabPanel>
|
|
296
|
+
) ) }
|
|
297
|
+
</Tabs>
|
|
231
298
|
);
|
|
232
299
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const alphaButton = await screen.findByRole( 'button', {
|
|
236
|
-
name: /alpha button/i,
|
|
237
|
-
} );
|
|
300
|
+
// Alpha is automatically selected as the selected tab.
|
|
301
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
238
302
|
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
await press.Tab();
|
|
303
|
+
// Select Beta, make sure the correct tabpanel is rendered
|
|
304
|
+
await click( screen.getByRole( 'tab', { name: 'Beta' } ) );
|
|
242
305
|
expect(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
expect( alphaButton ).toHaveFocus();
|
|
249
|
-
} );
|
|
250
|
-
|
|
251
|
-
it( "should focus the first tab, even if disabled, when the current selected tab id doesn't match an existing one", async () => {
|
|
252
|
-
const TABS_WITH_ALPHA_DISABLED = TABS.map( ( tabObj ) =>
|
|
253
|
-
tabObj.tabId === 'alpha'
|
|
254
|
-
? {
|
|
255
|
-
...tabObj,
|
|
256
|
-
tab: {
|
|
257
|
-
...tabObj.tab,
|
|
258
|
-
disabled: true,
|
|
259
|
-
},
|
|
260
|
-
}
|
|
261
|
-
: tabObj
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
await render(
|
|
265
|
-
<ControlledTabs
|
|
266
|
-
tabs={ TABS_WITH_ALPHA_DISABLED }
|
|
267
|
-
selectedTabId="non-existing-tab"
|
|
268
|
-
/>
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
// No tab should be selected i.e. it doesn't fall back to first tab.
|
|
272
|
-
await waitFor( () =>
|
|
273
|
-
expect(
|
|
274
|
-
screen.queryByRole( 'tab', { selected: true } )
|
|
275
|
-
).not.toBeInTheDocument()
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
// No tabpanel should be rendered either
|
|
279
|
-
expect( screen.queryByRole( 'tabpanel' ) ).not.toBeInTheDocument();
|
|
280
|
-
|
|
281
|
-
await press.Tab();
|
|
306
|
+
screen.getByRole( 'tab', {
|
|
307
|
+
selected: true,
|
|
308
|
+
name: 'Beta',
|
|
309
|
+
} )
|
|
310
|
+
).toBeVisible();
|
|
282
311
|
expect(
|
|
283
|
-
|
|
284
|
-
|
|
312
|
+
screen.getByRole( 'tabpanel', {
|
|
313
|
+
name: 'Beta',
|
|
314
|
+
} )
|
|
315
|
+
).toBeVisible();
|
|
285
316
|
|
|
286
|
-
|
|
317
|
+
// Select Gamma, make sure the correct tabpanel is rendered
|
|
318
|
+
await click( screen.getByRole( 'tab', { name: 'Gamma' } ) );
|
|
287
319
|
expect(
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
320
|
+
screen.getByRole( 'tab', {
|
|
321
|
+
selected: true,
|
|
322
|
+
name: 'Gamma',
|
|
323
|
+
} )
|
|
324
|
+
).toBeVisible();
|
|
292
325
|
expect(
|
|
293
|
-
|
|
294
|
-
|
|
326
|
+
screen.getByRole( 'tabpanel', {
|
|
327
|
+
name: 'Gamma',
|
|
328
|
+
} )
|
|
329
|
+
).toBeVisible();
|
|
295
330
|
|
|
296
|
-
|
|
297
|
-
await
|
|
331
|
+
// Select Delta, make sure the correct tabpanel is rendered
|
|
332
|
+
await click( screen.getByRole( 'tab', { name: 'Delta' } ) );
|
|
333
|
+
expect(
|
|
334
|
+
screen.getByRole( 'tab', {
|
|
335
|
+
selected: true,
|
|
336
|
+
name: 'Delta',
|
|
337
|
+
} )
|
|
338
|
+
).toBeVisible();
|
|
298
339
|
expect(
|
|
299
|
-
|
|
300
|
-
|
|
340
|
+
screen.getByRole( 'tabpanel', {
|
|
341
|
+
name: 'Delta',
|
|
342
|
+
} )
|
|
343
|
+
).toBeVisible();
|
|
301
344
|
} );
|
|
302
|
-
} );
|
|
303
345
|
|
|
304
|
-
describe( 'Tab Attributes', () => {
|
|
305
346
|
it( "should apply the tab's `className` to the tab button", async () => {
|
|
306
347
|
await render( <UncontrolledTabs tabs={ TABS } /> );
|
|
307
348
|
|
|
349
|
+
// Alpha is automatically selected as the selected tab.
|
|
350
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
351
|
+
|
|
308
352
|
expect(
|
|
309
353
|
await screen.findByRole( 'tab', { name: 'Alpha' } )
|
|
310
354
|
).toHaveClass( 'alpha-class' );
|
|
@@ -317,908 +361,1076 @@ describe( 'Tabs', () => {
|
|
|
317
361
|
} );
|
|
318
362
|
} );
|
|
319
363
|
|
|
320
|
-
describe( '
|
|
321
|
-
it( '
|
|
364
|
+
describe( 'pointer interactions', () => {
|
|
365
|
+
it( 'should select a tab when clicked', async () => {
|
|
322
366
|
const mockOnSelect = jest.fn();
|
|
323
367
|
|
|
324
368
|
await render(
|
|
325
369
|
<UncontrolledTabs tabs={ TABS } onSelect={ mockOnSelect } />
|
|
326
370
|
);
|
|
327
371
|
|
|
328
|
-
// Alpha is the
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
).toBeInTheDocument();
|
|
372
|
+
// Alpha is automatically selected as the selected tab.
|
|
373
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
374
|
+
|
|
375
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
333
376
|
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
334
377
|
|
|
335
378
|
// Click on Beta, make sure beta is the selected tab
|
|
336
379
|
await click( screen.getByRole( 'tab', { name: 'Beta' } ) );
|
|
337
380
|
|
|
338
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
|
|
339
381
|
expect(
|
|
340
|
-
screen.getByRole( '
|
|
341
|
-
|
|
382
|
+
screen.getByRole( 'tab', {
|
|
383
|
+
selected: true,
|
|
384
|
+
name: 'Beta',
|
|
385
|
+
} )
|
|
386
|
+
).toBeVisible();
|
|
387
|
+
expect(
|
|
388
|
+
screen.getByRole( 'tabpanel', {
|
|
389
|
+
name: 'Beta',
|
|
390
|
+
} )
|
|
391
|
+
).toBeVisible();
|
|
392
|
+
|
|
393
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
342
394
|
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
|
|
343
395
|
|
|
344
|
-
// Click on Alpha, make sure
|
|
396
|
+
// Click on Alpha, make sure alpha is the selected tab
|
|
345
397
|
await click( screen.getByRole( 'tab', { name: 'Alpha' } ) );
|
|
346
398
|
|
|
347
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
|
|
348
399
|
expect(
|
|
349
|
-
screen.getByRole( '
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
);
|
|
360
|
-
|
|
361
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
|
|
362
|
-
|
|
363
|
-
// onSelect gets called on the initial render. It should be called
|
|
364
|
-
// with the first enabled tab, which is alpha.
|
|
365
|
-
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
366
|
-
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
367
|
-
|
|
368
|
-
// Tab to focus the tablist. Make sure alpha is focused.
|
|
369
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
|
|
370
|
-
expect( await getSelectedTab() ).not.toHaveFocus();
|
|
371
|
-
await press.Tab();
|
|
372
|
-
expect( await getSelectedTab() ).toHaveFocus();
|
|
373
|
-
|
|
374
|
-
// Navigate forward with arrow keys and make sure the Beta tab is
|
|
375
|
-
// selected automatically.
|
|
376
|
-
await press.ArrowRight();
|
|
377
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
|
|
378
|
-
expect( await getSelectedTab() ).toHaveFocus();
|
|
379
|
-
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
380
|
-
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
|
|
400
|
+
screen.getByRole( 'tab', {
|
|
401
|
+
selected: true,
|
|
402
|
+
name: 'Alpha',
|
|
403
|
+
} )
|
|
404
|
+
).toBeVisible();
|
|
405
|
+
expect(
|
|
406
|
+
screen.getByRole( 'tabpanel', {
|
|
407
|
+
name: 'Alpha',
|
|
408
|
+
} )
|
|
409
|
+
).toBeVisible();
|
|
381
410
|
|
|
382
|
-
// Navigate backwards with arrow keys. Make sure alpha is
|
|
383
|
-
// selected automatically.
|
|
384
|
-
await press.ArrowLeft();
|
|
385
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
|
|
386
|
-
expect( await getSelectedTab() ).toHaveFocus();
|
|
387
411
|
expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
|
|
388
412
|
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
389
413
|
} );
|
|
390
414
|
|
|
391
|
-
it( '
|
|
415
|
+
it( 'should not select a disabled tab when clicked', async () => {
|
|
392
416
|
const mockOnSelect = jest.fn();
|
|
393
417
|
|
|
394
418
|
await render(
|
|
395
|
-
<UncontrolledTabs
|
|
419
|
+
<UncontrolledTabs
|
|
420
|
+
tabs={ TABS_WITH_BETA_DISABLED }
|
|
421
|
+
onSelect={ mockOnSelect }
|
|
422
|
+
/>
|
|
396
423
|
);
|
|
397
424
|
|
|
398
|
-
|
|
399
|
-
|
|
425
|
+
// Alpha is automatically selected as the selected tab.
|
|
426
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
400
427
|
|
|
401
|
-
// onSelect gets called on the initial render.
|
|
402
428
|
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
403
429
|
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
404
430
|
|
|
405
|
-
//
|
|
406
|
-
|
|
407
|
-
|
|
431
|
+
// Clicking on Beta does not result in beta being selected
|
|
432
|
+
// because the tab is disabled.
|
|
433
|
+
await click( screen.getByRole( 'tab', { name: 'Beta' } ) );
|
|
408
434
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
expect(
|
|
435
|
+
expect(
|
|
436
|
+
screen.getByRole( 'tab', {
|
|
437
|
+
selected: true,
|
|
438
|
+
name: 'Alpha',
|
|
439
|
+
} )
|
|
440
|
+
).toBeVisible();
|
|
441
|
+
expect(
|
|
442
|
+
screen.getByRole( 'tabpanel', {
|
|
443
|
+
name: 'Alpha',
|
|
444
|
+
} )
|
|
445
|
+
).toBeVisible();
|
|
416
446
|
|
|
417
|
-
|
|
418
|
-
// selected automatically.
|
|
419
|
-
await press.ArrowRight();
|
|
420
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
|
|
421
|
-
expect( await getSelectedTab() ).toHaveFocus();
|
|
422
|
-
expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
|
|
423
|
-
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
447
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
424
448
|
} );
|
|
449
|
+
} );
|
|
425
450
|
|
|
426
|
-
|
|
427
|
-
|
|
451
|
+
describe( 'initial tab selection', () => {
|
|
452
|
+
describe( 'when a selected tab id is not specified', () => {
|
|
453
|
+
describe( 'when left `undefined` [Uncontrolled]', () => {
|
|
454
|
+
it( 'should choose the first tab as selected', async () => {
|
|
455
|
+
await render( <UncontrolledTabs tabs={ TABS } /> );
|
|
428
456
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
457
|
+
// Alpha is automatically selected as the selected tab.
|
|
458
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
459
|
+
'Alpha'
|
|
460
|
+
);
|
|
432
461
|
|
|
433
|
-
|
|
434
|
-
|
|
462
|
+
// Press tab. The selected tab (alpha) received focus.
|
|
463
|
+
await press.Tab();
|
|
464
|
+
expect(
|
|
465
|
+
await screen.findByRole( 'tab', {
|
|
466
|
+
selected: true,
|
|
467
|
+
name: 'Alpha',
|
|
468
|
+
} )
|
|
469
|
+
).toHaveFocus();
|
|
470
|
+
} );
|
|
471
|
+
|
|
472
|
+
it( 'should choose the first non-disabled tab if the first tab is disabled', async () => {
|
|
473
|
+
await render(
|
|
474
|
+
<UncontrolledTabs tabs={ TABS_WITH_ALPHA_DISABLED } />
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
// Beta is automatically selected as the selected tab, since alpha is
|
|
478
|
+
// disabled.
|
|
479
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
480
|
+
'Beta'
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
// Press tab. The selected tab (beta) received focus. The corresponding
|
|
484
|
+
// tabpanel is shown.
|
|
485
|
+
await press.Tab();
|
|
486
|
+
expect(
|
|
487
|
+
await screen.findByRole( 'tab', {
|
|
488
|
+
selected: true,
|
|
489
|
+
name: 'Beta',
|
|
490
|
+
} )
|
|
491
|
+
).toHaveFocus();
|
|
492
|
+
} );
|
|
493
|
+
} );
|
|
494
|
+
describe( 'when `null` [Controlled]', () => {
|
|
495
|
+
it( 'should not have a selected tab nor show any tabpanels, make the tablist tabbable and still allow selecting tabs', async () => {
|
|
496
|
+
await render(
|
|
497
|
+
<ControlledTabs tabs={ TABS } selectedTabId={ null } />
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
// No initially selected tabs or tabpanels.
|
|
501
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
502
|
+
undefined
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
// Press tab. The tablist receives focus
|
|
506
|
+
await press.Tab();
|
|
507
|
+
expect(
|
|
508
|
+
await screen.findByRole( 'tablist' )
|
|
509
|
+
).toHaveFocus();
|
|
435
510
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
511
|
+
// Press right arrow to select the first tab (alpha) and
|
|
512
|
+
// show the related tabpanel.
|
|
513
|
+
await press.ArrowRight();
|
|
514
|
+
expect(
|
|
515
|
+
await screen.findByRole( 'tab', {
|
|
516
|
+
selected: true,
|
|
517
|
+
name: 'Alpha',
|
|
518
|
+
} )
|
|
519
|
+
).toHaveFocus();
|
|
520
|
+
expect(
|
|
521
|
+
await screen.findByRole( 'tabpanel', {
|
|
522
|
+
name: 'Alpha',
|
|
523
|
+
} )
|
|
524
|
+
).toBeVisible();
|
|
525
|
+
} );
|
|
526
|
+
} );
|
|
527
|
+
} );
|
|
439
528
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
529
|
+
describe( 'when a selected tab id is specified', () => {
|
|
530
|
+
describe( 'through the `defaultTabId` prop [Uncontrolled]', () => {
|
|
531
|
+
it( 'should select the initial tab matching the `defaultTabId` prop', async () => {
|
|
532
|
+
await render(
|
|
533
|
+
<UncontrolledTabs tabs={ TABS } defaultTabId="beta" />
|
|
534
|
+
);
|
|
535
|
+
|
|
536
|
+
// Beta is the initially selected tab
|
|
537
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
538
|
+
'Beta'
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
// Press tab. The selected tab (beta) received focus. The corresponding
|
|
542
|
+
// tabpanel is shown.
|
|
543
|
+
await press.Tab();
|
|
544
|
+
expect(
|
|
545
|
+
await screen.findByRole( 'tab', {
|
|
546
|
+
selected: true,
|
|
547
|
+
name: 'Beta',
|
|
548
|
+
} )
|
|
549
|
+
).toHaveFocus();
|
|
550
|
+
} );
|
|
551
|
+
|
|
552
|
+
it( 'should select the initial tab matching the `defaultTabId` prop even if the tab is disabled', async () => {
|
|
553
|
+
await render(
|
|
554
|
+
<UncontrolledTabs
|
|
555
|
+
tabs={ TABS_WITH_BETA_DISABLED }
|
|
556
|
+
defaultTabId="beta"
|
|
557
|
+
/>
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
// Beta is automatically selected as the selected tab despite being
|
|
561
|
+
// disabled, respecting the `defaultTabId` prop.
|
|
562
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
563
|
+
'Beta'
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
// Press tab. The selected tab (beta) received focus, since it is
|
|
567
|
+
// accessible despite being disabled.
|
|
568
|
+
await press.Tab();
|
|
569
|
+
expect(
|
|
570
|
+
await screen.findByRole( 'tab', {
|
|
571
|
+
selected: true,
|
|
572
|
+
name: 'Beta',
|
|
573
|
+
} )
|
|
574
|
+
).toHaveFocus();
|
|
575
|
+
} );
|
|
576
|
+
|
|
577
|
+
it( 'should not have a selected tab nor show any tabpanels, but allow tabbing to the first tab when `defaultTabId` prop does not match any known tab', async () => {
|
|
578
|
+
await render(
|
|
579
|
+
<UncontrolledTabs
|
|
580
|
+
tabs={ TABS }
|
|
581
|
+
defaultTabId="non-existing-tab"
|
|
582
|
+
/>
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
// No initially selected tabs or tabpanels, since the `defaultTabId`
|
|
586
|
+
// prop is not matching any known tabs.
|
|
587
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
588
|
+
undefined
|
|
589
|
+
);
|
|
590
|
+
|
|
591
|
+
// Press tab. The first tab receives focus, but it's
|
|
592
|
+
// not selected.
|
|
593
|
+
await press.Tab();
|
|
594
|
+
expect(
|
|
595
|
+
screen.getByRole( 'tab', { name: 'Alpha' } )
|
|
596
|
+
).toHaveFocus();
|
|
597
|
+
await waitFor( () =>
|
|
598
|
+
expect(
|
|
599
|
+
screen.queryByRole( 'tab', { selected: true } )
|
|
600
|
+
).not.toBeInTheDocument()
|
|
601
|
+
);
|
|
602
|
+
await waitFor( () =>
|
|
603
|
+
expect(
|
|
604
|
+
screen.queryByRole( 'tabpanel' )
|
|
605
|
+
).not.toBeInTheDocument()
|
|
606
|
+
);
|
|
443
607
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
608
|
+
// Press right arrow to select the next tab (beta) and
|
|
609
|
+
// show the related tabpanel.
|
|
610
|
+
await press.ArrowRight();
|
|
611
|
+
expect(
|
|
612
|
+
await screen.findByRole( 'tab', {
|
|
613
|
+
selected: true,
|
|
614
|
+
name: 'Beta',
|
|
615
|
+
} )
|
|
616
|
+
).toHaveFocus();
|
|
617
|
+
expect(
|
|
618
|
+
await screen.findByRole( 'tabpanel', {
|
|
619
|
+
name: 'Beta',
|
|
620
|
+
} )
|
|
621
|
+
).toBeVisible();
|
|
622
|
+
} );
|
|
623
|
+
|
|
624
|
+
it( 'should not have a selected tab nor show any tabpanels, but allow tabbing to the first tab, even when disabled, when `defaultTabId` prop does not match any known tab', async () => {
|
|
625
|
+
await render(
|
|
626
|
+
<UncontrolledTabs
|
|
627
|
+
tabs={ TABS_WITH_ALPHA_DISABLED }
|
|
628
|
+
defaultTabId="non-existing-tab"
|
|
629
|
+
/>
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
// No initially selected tabs or tabpanels, since the `defaultTabId`
|
|
633
|
+
// prop is not matching any known tabs.
|
|
634
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
635
|
+
undefined
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
// Press tab. The first tab receives focus, but it's
|
|
639
|
+
// not selected.
|
|
640
|
+
await press.Tab();
|
|
641
|
+
expect(
|
|
642
|
+
screen.getByRole( 'tab', { name: 'Alpha' } )
|
|
643
|
+
).toHaveFocus();
|
|
644
|
+
await waitFor( () =>
|
|
645
|
+
expect(
|
|
646
|
+
screen.queryByRole( 'tab', { selected: true } )
|
|
647
|
+
).not.toBeInTheDocument()
|
|
648
|
+
);
|
|
649
|
+
await waitFor( () =>
|
|
650
|
+
expect(
|
|
651
|
+
screen.queryByRole( 'tabpanel' )
|
|
652
|
+
).not.toBeInTheDocument()
|
|
653
|
+
);
|
|
450
654
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
655
|
+
// Press right arrow to select the next tab (beta) and
|
|
656
|
+
// show the related tabpanel.
|
|
657
|
+
await press.ArrowRight();
|
|
658
|
+
expect(
|
|
659
|
+
await screen.findByRole( 'tab', {
|
|
660
|
+
selected: true,
|
|
661
|
+
name: 'Beta',
|
|
662
|
+
} )
|
|
663
|
+
).toHaveFocus();
|
|
664
|
+
expect(
|
|
665
|
+
await screen.findByRole( 'tabpanel', {
|
|
666
|
+
name: 'Beta',
|
|
667
|
+
} )
|
|
668
|
+
).toBeVisible();
|
|
669
|
+
} );
|
|
670
|
+
|
|
671
|
+
it( 'should ignore any changes to the `defaultTabId` prop after the first render', async () => {
|
|
672
|
+
const mockOnSelect = jest.fn();
|
|
673
|
+
|
|
674
|
+
const { rerender } = await render(
|
|
675
|
+
<UncontrolledTabs
|
|
676
|
+
tabs={ TABS }
|
|
677
|
+
defaultTabId="beta"
|
|
678
|
+
onSelect={ mockOnSelect }
|
|
679
|
+
/>
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
// Beta is the initially selected tab
|
|
683
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
684
|
+
'Beta'
|
|
685
|
+
);
|
|
686
|
+
|
|
687
|
+
// Changing the defaultTabId prop to gamma should not have any effect.
|
|
688
|
+
await rerender(
|
|
689
|
+
<UncontrolledTabs
|
|
690
|
+
tabs={ TABS }
|
|
691
|
+
defaultTabId="gamma"
|
|
692
|
+
onSelect={ mockOnSelect }
|
|
693
|
+
/>
|
|
694
|
+
);
|
|
457
695
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
696
|
+
expect(
|
|
697
|
+
await screen.findByRole( 'tab', {
|
|
698
|
+
selected: true,
|
|
699
|
+
name: 'Beta',
|
|
700
|
+
} )
|
|
701
|
+
).toBeVisible();
|
|
702
|
+
expect(
|
|
703
|
+
screen.getByRole( 'tabpanel', {
|
|
704
|
+
name: 'Beta',
|
|
705
|
+
} )
|
|
706
|
+
).toBeVisible();
|
|
467
707
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
);
|
|
708
|
+
expect( mockOnSelect ).not.toHaveBeenCalled();
|
|
709
|
+
} );
|
|
710
|
+
} );
|
|
472
711
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
712
|
+
describe( 'through the `selectedTabId` prop [Controlled]', () => {
|
|
713
|
+
describe( 'when the `selectedTabId` matches an existing tab', () => {
|
|
714
|
+
it( 'should choose the initial tab matching the `selectedTabId`', async () => {
|
|
715
|
+
await render(
|
|
716
|
+
<ControlledTabs
|
|
717
|
+
tabs={ TABS }
|
|
718
|
+
selectedTabId="beta"
|
|
719
|
+
/>
|
|
720
|
+
);
|
|
476
721
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
expect( await getSelectedTab() ).toHaveFocus();
|
|
482
|
-
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
483
|
-
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
|
|
722
|
+
// Beta is the initially selected tab
|
|
723
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
724
|
+
'Beta'
|
|
725
|
+
);
|
|
484
726
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
727
|
+
// Press tab. The selected tab (beta) received focus, since it is
|
|
728
|
+
// accessible despite being disabled.
|
|
729
|
+
await press.Tab();
|
|
730
|
+
expect(
|
|
731
|
+
await screen.findByRole( 'tab', {
|
|
732
|
+
selected: true,
|
|
733
|
+
name: 'Beta',
|
|
734
|
+
} )
|
|
735
|
+
).toHaveFocus();
|
|
736
|
+
} );
|
|
492
737
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
// Navigate backwards with arrow keys. Make sure alpha is
|
|
502
|
-
// selected automatically.
|
|
503
|
-
await press.ArrowDown();
|
|
504
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
|
|
505
|
-
expect( await getSelectedTab() ).toHaveFocus();
|
|
506
|
-
expect( mockOnSelect ).toHaveBeenCalledTimes( 5 );
|
|
507
|
-
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
508
|
-
} );
|
|
738
|
+
it( 'should choose the initial tab matching the `selectedTabId` even if a `defaultTabId` is passed', async () => {
|
|
739
|
+
await render(
|
|
740
|
+
<ControlledTabs
|
|
741
|
+
tabs={ TABS }
|
|
742
|
+
defaultTabId="beta"
|
|
743
|
+
selectedTabId="gamma"
|
|
744
|
+
/>
|
|
745
|
+
);
|
|
509
746
|
|
|
510
|
-
|
|
511
|
-
|
|
747
|
+
// Gamma is the initially selected tab
|
|
748
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
749
|
+
'Gamma'
|
|
750
|
+
);
|
|
512
751
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
tab
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
);
|
|
752
|
+
// Press tab. The selected tab (gamma) received focus, since it is
|
|
753
|
+
// accessible despite being disabled.
|
|
754
|
+
await press.Tab();
|
|
755
|
+
expect(
|
|
756
|
+
await screen.findByRole( 'tab', {
|
|
757
|
+
selected: true,
|
|
758
|
+
name: 'Gamma',
|
|
759
|
+
} )
|
|
760
|
+
).toHaveFocus();
|
|
761
|
+
} );
|
|
524
762
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
763
|
+
it( 'should choose the initial tab matching the `selectedTabId` even if the tab is disabled', async () => {
|
|
764
|
+
await render(
|
|
765
|
+
<ControlledTabs
|
|
766
|
+
tabs={ TABS_WITH_BETA_DISABLED }
|
|
767
|
+
selectedTabId="beta"
|
|
768
|
+
/>
|
|
769
|
+
);
|
|
531
770
|
|
|
532
|
-
|
|
533
|
-
|
|
771
|
+
// Beta is the initially selected tab
|
|
772
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
773
|
+
'Beta'
|
|
774
|
+
);
|
|
534
775
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
776
|
+
// Press tab. The selected tab (beta) received focus, since it is
|
|
777
|
+
// accessible despite being disabled.
|
|
778
|
+
await press.Tab();
|
|
779
|
+
expect(
|
|
780
|
+
await screen.findByRole( 'tab', {
|
|
781
|
+
selected: true,
|
|
782
|
+
name: 'Beta',
|
|
783
|
+
} )
|
|
784
|
+
).toHaveFocus();
|
|
785
|
+
} );
|
|
786
|
+
} );
|
|
538
787
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
788
|
+
describe( "when the `selectedTabId` doesn't match an existing tab", () => {
|
|
789
|
+
it( 'should not have a selected tab nor show any tabpanels, but allow tabbing to the first tab', async () => {
|
|
790
|
+
await render(
|
|
791
|
+
<ControlledTabs
|
|
792
|
+
tabs={ TABS }
|
|
793
|
+
selectedTabId="non-existing-tab"
|
|
794
|
+
/>
|
|
795
|
+
);
|
|
542
796
|
|
|
543
|
-
|
|
544
|
-
|
|
797
|
+
// No initially selected tabs or tabpanels, since the `selectedTabId`
|
|
798
|
+
// prop is not matching any known tabs.
|
|
799
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
800
|
+
undefined
|
|
801
|
+
);
|
|
545
802
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
// The `mockOnSelect` callback doesn't fire, since the gamma tab was
|
|
563
|
-
// already selected.
|
|
564
|
-
await press.ArrowLeft();
|
|
565
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
|
|
566
|
-
expect( await getSelectedTab() ).toHaveFocus();
|
|
567
|
-
expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
|
|
803
|
+
// Press tab. The first tab receives focus, but it's
|
|
804
|
+
// not selected.
|
|
805
|
+
await press.Tab();
|
|
806
|
+
expect(
|
|
807
|
+
screen.getByRole( 'tab', { name: 'Alpha' } )
|
|
808
|
+
).toHaveFocus();
|
|
809
|
+
await waitFor( () =>
|
|
810
|
+
expect(
|
|
811
|
+
screen.queryByRole( 'tab', { selected: true } )
|
|
812
|
+
).not.toBeInTheDocument()
|
|
813
|
+
);
|
|
814
|
+
await waitFor( () =>
|
|
815
|
+
expect(
|
|
816
|
+
screen.queryByRole( 'tabpanel' )
|
|
817
|
+
).not.toBeInTheDocument()
|
|
818
|
+
);
|
|
568
819
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
820
|
+
// Press right arrow to select the next tab (beta) and
|
|
821
|
+
// show the related tabpanel.
|
|
822
|
+
await press.ArrowRight();
|
|
823
|
+
expect(
|
|
824
|
+
await screen.findByRole( 'tab', {
|
|
825
|
+
selected: true,
|
|
826
|
+
name: 'Beta',
|
|
827
|
+
} )
|
|
828
|
+
).toHaveFocus();
|
|
829
|
+
expect(
|
|
830
|
+
await screen.findByRole( 'tabpanel', {
|
|
831
|
+
name: 'Beta',
|
|
832
|
+
} )
|
|
833
|
+
).toBeVisible();
|
|
834
|
+
} );
|
|
577
835
|
|
|
578
|
-
|
|
579
|
-
|
|
836
|
+
it( 'should not have a selected tab nor show any tabpanels, but allow tabbing to the first tab even when disabled', async () => {
|
|
837
|
+
await render(
|
|
838
|
+
<ControlledTabs
|
|
839
|
+
tabs={ TABS_WITH_ALPHA_DISABLED }
|
|
840
|
+
selectedTabId="non-existing-tab"
|
|
841
|
+
/>
|
|
842
|
+
);
|
|
580
843
|
|
|
581
|
-
|
|
582
|
-
|
|
844
|
+
// No initially selected tabs or tabpanels, since the `selectedTabId`
|
|
845
|
+
// prop is not matching any known tabs.
|
|
846
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
847
|
+
undefined
|
|
848
|
+
);
|
|
583
849
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
850
|
+
// Press tab. The first tab receives focus, but it's
|
|
851
|
+
// not selected.
|
|
852
|
+
await press.Tab();
|
|
853
|
+
expect(
|
|
854
|
+
screen.getByRole( 'tab', { name: 'Alpha' } )
|
|
855
|
+
).toHaveFocus();
|
|
856
|
+
await waitFor( () =>
|
|
857
|
+
expect(
|
|
858
|
+
screen.queryByRole( 'tab', { selected: true } )
|
|
859
|
+
).not.toBeInTheDocument()
|
|
860
|
+
);
|
|
861
|
+
await waitFor( () =>
|
|
862
|
+
expect(
|
|
863
|
+
screen.queryByRole( 'tabpanel' )
|
|
864
|
+
).not.toBeInTheDocument()
|
|
865
|
+
);
|
|
590
866
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
867
|
+
// Press right arrow to select the next tab (beta) and
|
|
868
|
+
// show the related tabpanel.
|
|
869
|
+
await press.ArrowRight();
|
|
870
|
+
expect(
|
|
871
|
+
await screen.findByRole( 'tab', {
|
|
872
|
+
selected: true,
|
|
873
|
+
name: 'Beta',
|
|
874
|
+
} )
|
|
875
|
+
).toHaveFocus();
|
|
876
|
+
expect(
|
|
877
|
+
await screen.findByRole( 'tabpanel', {
|
|
878
|
+
name: 'Beta',
|
|
879
|
+
} )
|
|
880
|
+
).toBeVisible();
|
|
881
|
+
} );
|
|
882
|
+
} );
|
|
883
|
+
} );
|
|
600
884
|
} );
|
|
885
|
+
} );
|
|
601
886
|
|
|
602
|
-
|
|
603
|
-
|
|
887
|
+
describe( 'keyboard interactions', () => {
|
|
888
|
+
describe.each( [
|
|
889
|
+
[ 'Uncontrolled', UncontrolledTabs ],
|
|
890
|
+
[ 'Controlled', ControlledTabs ],
|
|
891
|
+
] )( '[`%s`]', ( _mode, Component ) => {
|
|
892
|
+
it( 'should handle the tablist as one tab stop', async () => {
|
|
893
|
+
await render( <Component tabs={ TABS } /> );
|
|
604
894
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
tabs={ TABS }
|
|
608
|
-
onSelect={ mockOnSelect }
|
|
609
|
-
selectOnMove={ false }
|
|
610
|
-
/>
|
|
611
|
-
);
|
|
895
|
+
// Alpha is automatically selected as the selected tab.
|
|
896
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
612
897
|
|
|
613
|
-
|
|
614
|
-
|
|
898
|
+
// Press tab. The selected tab (alpha) received focus.
|
|
899
|
+
await press.Tab();
|
|
900
|
+
expect(
|
|
901
|
+
await screen.findByRole( 'tab', {
|
|
902
|
+
selected: true,
|
|
903
|
+
name: 'Alpha',
|
|
904
|
+
} )
|
|
905
|
+
).toHaveFocus();
|
|
615
906
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
907
|
+
// By default the tabpanel should receive focus
|
|
908
|
+
await press.Tab();
|
|
909
|
+
expect(
|
|
910
|
+
await screen.findByRole( 'tabpanel', {
|
|
911
|
+
name: 'Alpha',
|
|
912
|
+
} )
|
|
913
|
+
).toHaveFocus();
|
|
914
|
+
} );
|
|
619
915
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
await press.Enter();
|
|
640
|
-
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
641
|
-
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
|
|
916
|
+
it( 'should not focus the tabpanel container when its `focusable` property is set to `false`', async () => {
|
|
917
|
+
await render(
|
|
918
|
+
<Component
|
|
919
|
+
tabs={ TABS.map( ( tabObj ) =>
|
|
920
|
+
tabObj.tabId === 'alpha'
|
|
921
|
+
? {
|
|
922
|
+
...tabObj,
|
|
923
|
+
content: (
|
|
924
|
+
<>
|
|
925
|
+
Selected Tab: Alpha
|
|
926
|
+
<button>Alpha Button</button>
|
|
927
|
+
</>
|
|
928
|
+
),
|
|
929
|
+
tabpanel: { focusable: false },
|
|
930
|
+
}
|
|
931
|
+
: tabObj
|
|
932
|
+
) }
|
|
933
|
+
/>
|
|
934
|
+
);
|
|
642
935
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
// spacebar or enter key. onSelect shouldn't fire since the selected
|
|
646
|
-
// tab didn't change.
|
|
647
|
-
await press.ArrowRight();
|
|
648
|
-
expect(
|
|
649
|
-
await screen.findByRole( 'tab', { name: 'Gamma' } )
|
|
650
|
-
).toHaveFocus();
|
|
651
|
-
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
652
|
-
expect(
|
|
653
|
-
screen.getByRole( 'tab', { name: 'Gamma' } )
|
|
654
|
-
).toHaveFocus();
|
|
936
|
+
// Alpha is automatically selected as the selected tab.
|
|
937
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
655
938
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
939
|
+
// Tab should initially focus the first tab in the tablist, which
|
|
940
|
+
// is Alpha.
|
|
941
|
+
await press.Tab();
|
|
942
|
+
expect(
|
|
943
|
+
await screen.findByRole( 'tab', {
|
|
944
|
+
selected: true,
|
|
945
|
+
name: 'Alpha',
|
|
946
|
+
} )
|
|
947
|
+
).toHaveFocus();
|
|
665
948
|
|
|
666
|
-
|
|
949
|
+
// In this case, the tabpanel container is skipped and focus is
|
|
950
|
+
// moved directly to its contents
|
|
951
|
+
await press.Tab();
|
|
667
952
|
expect(
|
|
668
|
-
await screen.findByRole( '
|
|
669
|
-
|
|
953
|
+
await screen.findByRole( 'button', {
|
|
954
|
+
name: 'Alpha Button',
|
|
955
|
+
} )
|
|
956
|
+
).toHaveFocus();
|
|
670
957
|
} );
|
|
671
|
-
it( 'should not have a selected tab if the currently selected tab is removed', async () => {
|
|
672
|
-
const { rerender } = await render(
|
|
673
|
-
<UncontrolledTabs tabs={ TABS } />
|
|
674
|
-
);
|
|
675
958
|
|
|
676
|
-
|
|
677
|
-
|
|
959
|
+
it( 'should select tabs in the tablist when using the left and right arrow keys by default (automatic tab activation)', async () => {
|
|
960
|
+
const mockOnSelect = jest.fn();
|
|
678
961
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
962
|
+
await render(
|
|
963
|
+
<Component tabs={ TABS } onSelect={ mockOnSelect } />
|
|
964
|
+
);
|
|
682
965
|
|
|
683
|
-
//
|
|
684
|
-
await
|
|
966
|
+
// Alpha is automatically selected as the selected tab.
|
|
967
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
685
968
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
expect(
|
|
689
|
-
screen.queryByRole( 'tab', { selected: true } )
|
|
690
|
-
).not.toBeInTheDocument()
|
|
691
|
-
);
|
|
969
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
970
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
692
971
|
|
|
693
|
-
//
|
|
972
|
+
// Focus the tablist (and the selected tab, alpha)
|
|
973
|
+
// Tab should initially focus the first tab in the tablist, which
|
|
974
|
+
// is Alpha.
|
|
975
|
+
await press.Tab();
|
|
694
976
|
expect(
|
|
695
|
-
screen.
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
977
|
+
await screen.findByRole( 'tab', {
|
|
978
|
+
selected: true,
|
|
979
|
+
name: 'Alpha',
|
|
980
|
+
} )
|
|
981
|
+
).toHaveFocus();
|
|
699
982
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
await render(
|
|
703
|
-
<UncontrolledTabs tabs={ TABS } defaultTabId="beta" />
|
|
704
|
-
);
|
|
983
|
+
// Press the right arrow key to select the beta tab
|
|
984
|
+
await press.ArrowRight();
|
|
705
985
|
|
|
706
|
-
expect(
|
|
707
|
-
|
|
986
|
+
expect(
|
|
987
|
+
screen.getByRole( 'tab', {
|
|
988
|
+
selected: true,
|
|
989
|
+
name: 'Beta',
|
|
990
|
+
} )
|
|
991
|
+
).toHaveFocus();
|
|
992
|
+
expect(
|
|
993
|
+
screen.getByRole( 'tabpanel', {
|
|
994
|
+
name: 'Beta',
|
|
995
|
+
} )
|
|
996
|
+
).toBeVisible();
|
|
708
997
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
<UncontrolledTabs
|
|
712
|
-
tabs={ TABS }
|
|
713
|
-
defaultTabId="does-not-exist"
|
|
714
|
-
/>
|
|
715
|
-
);
|
|
998
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
999
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
|
|
716
1000
|
|
|
717
|
-
//
|
|
718
|
-
|
|
719
|
-
screen.queryByRole( 'tab', { selected: true } )
|
|
720
|
-
).not.toBeInTheDocument();
|
|
1001
|
+
// Press the right arrow key to select the gamma tab
|
|
1002
|
+
await press.ArrowRight();
|
|
721
1003
|
|
|
722
|
-
// No tabpanel should be rendered either
|
|
723
1004
|
expect(
|
|
724
|
-
screen.
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
1005
|
+
screen.getByRole( 'tab', {
|
|
1006
|
+
selected: true,
|
|
1007
|
+
name: 'Gamma',
|
|
1008
|
+
} )
|
|
1009
|
+
).toHaveFocus();
|
|
1010
|
+
expect(
|
|
1011
|
+
screen.getByRole( 'tabpanel', {
|
|
1012
|
+
name: 'Gamma',
|
|
1013
|
+
} )
|
|
1014
|
+
).toBeVisible();
|
|
731
1015
|
|
|
732
|
-
expect(
|
|
1016
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
|
|
1017
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'gamma' );
|
|
733
1018
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
);
|
|
1019
|
+
// Press the left arrow key to select the beta tab
|
|
1020
|
+
await press.ArrowLeft();
|
|
737
1021
|
|
|
738
|
-
expect(
|
|
1022
|
+
expect(
|
|
1023
|
+
screen.getByRole( 'tab', {
|
|
1024
|
+
selected: true,
|
|
1025
|
+
name: 'Beta',
|
|
1026
|
+
} )
|
|
1027
|
+
).toHaveFocus();
|
|
1028
|
+
expect(
|
|
1029
|
+
screen.getByRole( 'tabpanel', {
|
|
1030
|
+
name: 'Beta',
|
|
1031
|
+
} )
|
|
1032
|
+
).toBeVisible();
|
|
1033
|
+
|
|
1034
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 4 );
|
|
1035
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
|
|
739
1036
|
} );
|
|
740
1037
|
|
|
741
|
-
it( 'should not
|
|
1038
|
+
it( 'should not automatically select tabs in the tablist when pressing the left and right arrow keys if the `selectOnMove` prop is set to `false` (manual tab activation)', async () => {
|
|
742
1039
|
const mockOnSelect = jest.fn();
|
|
743
1040
|
|
|
744
|
-
|
|
745
|
-
<
|
|
1041
|
+
await render(
|
|
1042
|
+
<Component
|
|
746
1043
|
tabs={ TABS }
|
|
747
|
-
defaultTabId="gamma"
|
|
748
1044
|
onSelect={ mockOnSelect }
|
|
1045
|
+
selectOnMove={ false }
|
|
749
1046
|
/>
|
|
750
1047
|
);
|
|
751
1048
|
|
|
752
|
-
|
|
1049
|
+
// Alpha is automatically selected as the selected tab.
|
|
1050
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
753
1051
|
|
|
754
|
-
|
|
755
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
|
|
1052
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
756
1053
|
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
757
1054
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
1055
|
+
// Focus the tablist (and the selected tab, alpha)
|
|
1056
|
+
// Tab should initially focus the first tab in the tablist, which
|
|
1057
|
+
// is Alpha.
|
|
1058
|
+
await press.Tab();
|
|
1059
|
+
expect(
|
|
1060
|
+
await screen.findByRole( 'tab', {
|
|
1061
|
+
selected: true,
|
|
1062
|
+
name: 'Alpha',
|
|
1063
|
+
} )
|
|
1064
|
+
).toHaveFocus();
|
|
765
1065
|
|
|
766
|
-
//
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1066
|
+
// Press the right arrow key to move focus to the beta tab,
|
|
1067
|
+
// but without selecting it
|
|
1068
|
+
await press.ArrowRight();
|
|
1069
|
+
|
|
1070
|
+
expect(
|
|
1071
|
+
screen.getByRole( 'tab', {
|
|
1072
|
+
selected: false,
|
|
1073
|
+
name: 'Beta',
|
|
1074
|
+
} )
|
|
1075
|
+
).toHaveFocus();
|
|
1076
|
+
expect(
|
|
1077
|
+
await screen.findByRole( 'tab', {
|
|
1078
|
+
selected: true,
|
|
1079
|
+
name: 'Alpha',
|
|
1080
|
+
} )
|
|
1081
|
+
).toBeVisible();
|
|
1082
|
+
expect(
|
|
1083
|
+
screen.getByRole( 'tabpanel', {
|
|
1084
|
+
name: 'Alpha',
|
|
1085
|
+
} )
|
|
1086
|
+
).toBeVisible();
|
|
1087
|
+
|
|
1088
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
772
1089
|
|
|
773
|
-
//
|
|
1090
|
+
// Press the space key to click the beta tab, and select it.
|
|
1091
|
+
// The same should be true with any other mean of clicking the tab button
|
|
1092
|
+
// (ie. mouse click, enter key).
|
|
1093
|
+
await press.Space();
|
|
1094
|
+
|
|
1095
|
+
expect(
|
|
1096
|
+
screen.getByRole( 'tab', {
|
|
1097
|
+
selected: true,
|
|
1098
|
+
name: 'Beta',
|
|
1099
|
+
} )
|
|
1100
|
+
).toHaveFocus();
|
|
774
1101
|
expect(
|
|
775
|
-
screen.
|
|
776
|
-
|
|
1102
|
+
screen.getByRole( 'tabpanel', {
|
|
1103
|
+
name: 'Beta',
|
|
1104
|
+
} )
|
|
1105
|
+
).toBeVisible();
|
|
1106
|
+
|
|
1107
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
1108
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
|
|
777
1109
|
} );
|
|
778
1110
|
|
|
779
|
-
it( 'should
|
|
1111
|
+
it( 'should not select tabs in the tablist when using the up and down arrow keys, unless the `orientation` prop is set to `vertical`', async () => {
|
|
780
1112
|
const mockOnSelect = jest.fn();
|
|
781
1113
|
|
|
782
1114
|
const { rerender } = await render(
|
|
783
|
-
<
|
|
784
|
-
tabs={ TABS }
|
|
785
|
-
defaultTabId="gamma"
|
|
786
|
-
onSelect={ mockOnSelect }
|
|
787
|
-
/>
|
|
1115
|
+
<Component tabs={ TABS } onSelect={ mockOnSelect } />
|
|
788
1116
|
);
|
|
789
1117
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
await click( screen.getByRole( 'tab', { name: 'Alpha' } ) );
|
|
793
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
|
|
1118
|
+
// Alpha is automatically selected as the selected tab.
|
|
1119
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
794
1120
|
|
|
795
1121
|
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
796
1122
|
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
797
1123
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
);
|
|
1124
|
+
// Focus the tablist (and the selected tab, alpha)
|
|
1125
|
+
// Tab should initially focus the first tab in the tablist, which
|
|
1126
|
+
// is Alpha.
|
|
1127
|
+
await press.Tab();
|
|
1128
|
+
expect(
|
|
1129
|
+
await screen.findByRole( 'tab', {
|
|
1130
|
+
selected: true,
|
|
1131
|
+
name: 'Alpha',
|
|
1132
|
+
} )
|
|
1133
|
+
).toHaveFocus();
|
|
809
1134
|
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
1135
|
+
// Press the up arrow key, but the focused/selected tab does not change.
|
|
1136
|
+
await press.ArrowUp();
|
|
1137
|
+
|
|
1138
|
+
expect(
|
|
1139
|
+
screen.getByRole( 'tab', {
|
|
1140
|
+
selected: true,
|
|
1141
|
+
name: 'Alpha',
|
|
1142
|
+
} )
|
|
1143
|
+
).toHaveFocus();
|
|
1144
|
+
expect(
|
|
1145
|
+
screen.getByRole( 'tabpanel', {
|
|
1146
|
+
name: 'Alpha',
|
|
1147
|
+
} )
|
|
1148
|
+
).toBeVisible();
|
|
817
1149
|
|
|
818
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
|
|
819
1150
|
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
820
|
-
} );
|
|
821
1151
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
1152
|
+
// Press the down arrow key, but the focused/selected tab does not change.
|
|
1153
|
+
await press.ArrowDown();
|
|
1154
|
+
|
|
1155
|
+
expect(
|
|
1156
|
+
screen.getByRole( 'tab', {
|
|
1157
|
+
selected: true,
|
|
1158
|
+
name: 'Alpha',
|
|
1159
|
+
} )
|
|
1160
|
+
).toHaveFocus();
|
|
1161
|
+
expect(
|
|
1162
|
+
screen.getByRole( 'tabpanel', {
|
|
1163
|
+
name: 'Alpha',
|
|
1164
|
+
} )
|
|
1165
|
+
).toBeVisible();
|
|
826
1166
|
|
|
827
|
-
expect(
|
|
1167
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
828
1168
|
|
|
829
|
-
//
|
|
1169
|
+
// Change the orientation to "vertical" and rerender the component.
|
|
830
1170
|
await rerender(
|
|
831
|
-
<
|
|
832
|
-
tabs={ TABS
|
|
833
|
-
|
|
1171
|
+
<Component
|
|
1172
|
+
tabs={ TABS }
|
|
1173
|
+
onSelect={ mockOnSelect }
|
|
1174
|
+
orientation="vertical"
|
|
834
1175
|
/>
|
|
835
1176
|
);
|
|
836
1177
|
|
|
837
|
-
|
|
838
|
-
|
|
1178
|
+
// Pressing the down arrow key now selects the next tab (beta).
|
|
1179
|
+
await press.ArrowDown();
|
|
1180
|
+
|
|
839
1181
|
expect(
|
|
840
|
-
screen.
|
|
841
|
-
|
|
842
|
-
|
|
1182
|
+
screen.getByRole( 'tab', {
|
|
1183
|
+
selected: true,
|
|
1184
|
+
name: 'Beta',
|
|
1185
|
+
} )
|
|
1186
|
+
).toHaveFocus();
|
|
843
1187
|
expect(
|
|
844
|
-
screen.
|
|
845
|
-
|
|
846
|
-
|
|
1188
|
+
screen.getByRole( 'tabpanel', {
|
|
1189
|
+
name: 'Beta',
|
|
1190
|
+
} )
|
|
1191
|
+
).toBeVisible();
|
|
847
1192
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
<UncontrolledTabs tabs={ TABS } defaultTabId="delta" />
|
|
851
|
-
);
|
|
1193
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
1194
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
|
|
852
1195
|
|
|
853
|
-
//
|
|
854
|
-
await
|
|
855
|
-
expect(
|
|
856
|
-
screen.queryByRole( 'tab', { selected: true } )
|
|
857
|
-
).not.toBeInTheDocument()
|
|
858
|
-
);
|
|
1196
|
+
// Pressing the up arrow key now selects the previous tab (alpha).
|
|
1197
|
+
await press.ArrowUp();
|
|
859
1198
|
|
|
860
|
-
// No tabpanel should be rendered either
|
|
861
1199
|
expect(
|
|
862
|
-
screen.
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
1200
|
+
screen.getByRole( 'tab', {
|
|
1201
|
+
selected: true,
|
|
1202
|
+
name: 'Alpha',
|
|
1203
|
+
} )
|
|
1204
|
+
).toHaveFocus();
|
|
1205
|
+
expect(
|
|
1206
|
+
screen.getByRole( 'tabpanel', {
|
|
1207
|
+
name: 'Alpha',
|
|
1208
|
+
} )
|
|
1209
|
+
).toBeVisible();
|
|
871
1210
|
|
|
872
|
-
expect(
|
|
1211
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
|
|
1212
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
873
1213
|
} );
|
|
874
|
-
} );
|
|
875
1214
|
|
|
876
|
-
|
|
877
|
-
it( 'should disable the tab when `disabled` is `true`', async () => {
|
|
1215
|
+
it( 'should loop tab focus at the end of the tablist when using arrow keys', async () => {
|
|
878
1216
|
const mockOnSelect = jest.fn();
|
|
879
1217
|
|
|
880
|
-
const TABS_WITH_DELTA_DISABLED = TABS_WITH_DELTA.map(
|
|
881
|
-
( tabObj ) =>
|
|
882
|
-
tabObj.tabId === 'delta'
|
|
883
|
-
? {
|
|
884
|
-
...tabObj,
|
|
885
|
-
tab: {
|
|
886
|
-
...tabObj.tab,
|
|
887
|
-
disabled: true,
|
|
888
|
-
},
|
|
889
|
-
}
|
|
890
|
-
: tabObj
|
|
891
|
-
);
|
|
892
|
-
|
|
893
1218
|
await render(
|
|
894
|
-
<
|
|
895
|
-
tabs={ TABS_WITH_DELTA_DISABLED }
|
|
896
|
-
onSelect={ mockOnSelect }
|
|
897
|
-
/>
|
|
1219
|
+
<Component tabs={ TABS } onSelect={ mockOnSelect } />
|
|
898
1220
|
);
|
|
899
1221
|
|
|
900
|
-
|
|
1222
|
+
// Alpha is automatically selected as the selected tab.
|
|
1223
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
901
1224
|
|
|
902
|
-
expect(
|
|
903
|
-
screen.getByRole( 'tab', { name: 'Delta' } )
|
|
904
|
-
).toHaveAttribute( 'aria-disabled', 'true' );
|
|
905
|
-
|
|
906
|
-
// onSelect gets called on the initial render.
|
|
907
1225
|
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
908
1226
|
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
909
1227
|
|
|
910
|
-
//
|
|
1228
|
+
// Focus the tablist (and the selected tab, alpha)
|
|
1229
|
+
// Tab should initially focus the first tab in the tablist, which
|
|
1230
|
+
// is Alpha.
|
|
911
1231
|
await press.Tab();
|
|
912
1232
|
expect(
|
|
913
|
-
screen.
|
|
1233
|
+
await screen.findByRole( 'tab', {
|
|
1234
|
+
selected: true,
|
|
1235
|
+
name: 'Alpha',
|
|
1236
|
+
} )
|
|
914
1237
|
).toHaveFocus();
|
|
915
1238
|
|
|
916
|
-
//
|
|
917
|
-
// highlighted, but not selected.
|
|
1239
|
+
// Press the left arrow key to loop around and select the gamma tab
|
|
918
1240
|
await press.ArrowLeft();
|
|
919
|
-
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
920
1241
|
|
|
921
|
-
// Delta (which is disabled) has focus
|
|
922
1242
|
expect(
|
|
923
|
-
screen.getByRole( 'tab', {
|
|
1243
|
+
screen.getByRole( 'tab', {
|
|
1244
|
+
selected: true,
|
|
1245
|
+
name: 'Gamma',
|
|
1246
|
+
} )
|
|
924
1247
|
).toHaveFocus();
|
|
1248
|
+
expect(
|
|
1249
|
+
screen.getByRole( 'tabpanel', {
|
|
1250
|
+
name: 'Gamma',
|
|
1251
|
+
} )
|
|
1252
|
+
).toBeVisible();
|
|
925
1253
|
|
|
926
|
-
|
|
927
|
-
expect(
|
|
928
|
-
} );
|
|
929
|
-
|
|
930
|
-
it( 'should select first enabled tab when the initial tab is disabled', async () => {
|
|
931
|
-
const TABS_WITH_ALPHA_DISABLED = TABS.map( ( tabObj ) =>
|
|
932
|
-
tabObj.tabId === 'alpha'
|
|
933
|
-
? {
|
|
934
|
-
...tabObj,
|
|
935
|
-
tab: {
|
|
936
|
-
...tabObj.tab,
|
|
937
|
-
disabled: true,
|
|
938
|
-
},
|
|
939
|
-
}
|
|
940
|
-
: tabObj
|
|
941
|
-
);
|
|
942
|
-
|
|
943
|
-
const { rerender } = await render(
|
|
944
|
-
<UncontrolledTabs tabs={ TABS_WITH_ALPHA_DISABLED } />
|
|
945
|
-
);
|
|
1254
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
1255
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'gamma' );
|
|
946
1256
|
|
|
947
|
-
//
|
|
948
|
-
|
|
949
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
|
|
1257
|
+
// Press the right arrow key to loop around and select the alpha tab
|
|
1258
|
+
await press.ArrowRight();
|
|
950
1259
|
|
|
951
|
-
|
|
952
|
-
|
|
1260
|
+
expect(
|
|
1261
|
+
screen.getByRole( 'tab', {
|
|
1262
|
+
selected: true,
|
|
1263
|
+
name: 'Alpha',
|
|
1264
|
+
} )
|
|
1265
|
+
).toHaveFocus();
|
|
1266
|
+
expect(
|
|
1267
|
+
screen.getByRole( 'tabpanel', {
|
|
1268
|
+
name: 'Alpha',
|
|
1269
|
+
} )
|
|
1270
|
+
).toBeVisible();
|
|
953
1271
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
|
|
1272
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
|
|
1273
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
957
1274
|
} );
|
|
958
1275
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
...tabObj,
|
|
964
|
-
tab: {
|
|
965
|
-
...tabObj.tab,
|
|
966
|
-
disabled: true,
|
|
967
|
-
},
|
|
968
|
-
}
|
|
969
|
-
: tabObj
|
|
970
|
-
);
|
|
971
|
-
const { rerender } = await render(
|
|
972
|
-
<UncontrolledTabs
|
|
973
|
-
tabs={ TABS_ONLY_GAMMA_ENABLED }
|
|
974
|
-
defaultTabId="beta"
|
|
975
|
-
/>
|
|
976
|
-
);
|
|
1276
|
+
// TODO: mock writing direction to RTL
|
|
1277
|
+
it( 'should swap the left and right arrow keys when selecting tabs if the writing direction is set to RTL', async () => {
|
|
1278
|
+
// For this test only, mock the writing direction to RTL.
|
|
1279
|
+
mockedIsRTL.mockImplementation( () => true );
|
|
977
1280
|
|
|
978
|
-
|
|
979
|
-
// disabled the first enabled tab should be gamma.
|
|
980
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
|
|
1281
|
+
const mockOnSelect = jest.fn();
|
|
981
1282
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
<UncontrolledTabs tabs={ TABS } defaultTabId="beta" />
|
|
1283
|
+
await render(
|
|
1284
|
+
<Component tabs={ TABS } onSelect={ mockOnSelect } />
|
|
985
1285
|
);
|
|
986
1286
|
|
|
987
|
-
//
|
|
988
|
-
|
|
989
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
|
|
990
|
-
} );
|
|
1287
|
+
// Alpha is automatically selected as the selected tab.
|
|
1288
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
991
1289
|
|
|
992
|
-
it( 'should keep the currently tab as selected even when it becomes disabled', async () => {
|
|
993
|
-
const mockOnSelect = jest.fn();
|
|
994
|
-
const { rerender } = await render(
|
|
995
|
-
<UncontrolledTabs tabs={ TABS } onSelect={ mockOnSelect } />
|
|
996
|
-
);
|
|
997
|
-
|
|
998
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Alpha' );
|
|
999
1290
|
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
1000
1291
|
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
1001
1292
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
);
|
|
1013
|
-
|
|
1014
|
-
// Disable alpha
|
|
1015
|
-
await rerender(
|
|
1016
|
-
<UncontrolledTabs
|
|
1017
|
-
tabs={ TABS_WITH_ALPHA_DISABLED }
|
|
1018
|
-
onSelect={ mockOnSelect }
|
|
1019
|
-
/>
|
|
1020
|
-
);
|
|
1293
|
+
// Focus the tablist (and the selected tab, alpha)
|
|
1294
|
+
// Tab should initially focus the first tab in the tablist, which
|
|
1295
|
+
// is Alpha.
|
|
1296
|
+
await press.Tab();
|
|
1297
|
+
expect(
|
|
1298
|
+
await screen.findByRole( 'tab', {
|
|
1299
|
+
selected: true,
|
|
1300
|
+
name: 'Alpha',
|
|
1301
|
+
} )
|
|
1302
|
+
).toHaveFocus();
|
|
1021
1303
|
|
|
1022
|
-
|
|
1023
|
-
|
|
1304
|
+
// Press the left arrow key to select the beta tab
|
|
1305
|
+
await press.ArrowLeft();
|
|
1024
1306
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1307
|
+
expect(
|
|
1308
|
+
screen.getByRole( 'tab', {
|
|
1309
|
+
selected: true,
|
|
1310
|
+
name: 'Beta',
|
|
1311
|
+
} )
|
|
1312
|
+
).toHaveFocus();
|
|
1313
|
+
expect(
|
|
1314
|
+
screen.getByRole( 'tabpanel', {
|
|
1315
|
+
name: 'Beta',
|
|
1316
|
+
} )
|
|
1317
|
+
).toBeVisible();
|
|
1029
1318
|
|
|
1030
|
-
expect(
|
|
1031
|
-
expect( mockOnSelect ).
|
|
1032
|
-
} );
|
|
1319
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
1320
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
|
|
1033
1321
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1322
|
+
// Press the left arrow key to select the gamma tab
|
|
1323
|
+
await press.ArrowLeft();
|
|
1036
1324
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1325
|
+
expect(
|
|
1326
|
+
screen.getByRole( 'tab', {
|
|
1327
|
+
selected: true,
|
|
1328
|
+
name: 'Gamma',
|
|
1329
|
+
} )
|
|
1330
|
+
).toHaveFocus();
|
|
1331
|
+
expect(
|
|
1332
|
+
screen.getByRole( 'tabpanel', {
|
|
1333
|
+
name: 'Gamma',
|
|
1334
|
+
} )
|
|
1335
|
+
).toBeVisible();
|
|
1044
1336
|
|
|
1045
|
-
expect(
|
|
1046
|
-
|
|
1047
|
-
const TABS_WITH_GAMMA_DISABLED = TABS.map( ( tabObj ) =>
|
|
1048
|
-
tabObj.tabId === 'gamma'
|
|
1049
|
-
? {
|
|
1050
|
-
...tabObj,
|
|
1051
|
-
tab: {
|
|
1052
|
-
...tabObj.tab,
|
|
1053
|
-
disabled: true,
|
|
1054
|
-
},
|
|
1055
|
-
}
|
|
1056
|
-
: tabObj
|
|
1057
|
-
);
|
|
1337
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 3 );
|
|
1338
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'gamma' );
|
|
1058
1339
|
|
|
1059
|
-
//
|
|
1060
|
-
await
|
|
1061
|
-
<UncontrolledTabs
|
|
1062
|
-
tabs={ TABS_WITH_GAMMA_DISABLED }
|
|
1063
|
-
onSelect={ mockOnSelect }
|
|
1064
|
-
defaultTabId="gamma"
|
|
1065
|
-
/>
|
|
1066
|
-
);
|
|
1340
|
+
// Press the right arrow key to select the beta tab
|
|
1341
|
+
await press.ArrowRight();
|
|
1067
1342
|
|
|
1068
|
-
expect(
|
|
1343
|
+
expect(
|
|
1344
|
+
screen.getByRole( 'tab', {
|
|
1345
|
+
selected: true,
|
|
1346
|
+
name: 'Beta',
|
|
1347
|
+
} )
|
|
1348
|
+
).toHaveFocus();
|
|
1349
|
+
expect(
|
|
1350
|
+
screen.getByRole( 'tabpanel', {
|
|
1351
|
+
name: 'Beta',
|
|
1352
|
+
} )
|
|
1353
|
+
).toBeVisible();
|
|
1069
1354
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
<UncontrolledTabs
|
|
1073
|
-
tabs={ TABS }
|
|
1074
|
-
onSelect={ mockOnSelect }
|
|
1075
|
-
defaultTabId="gamma"
|
|
1076
|
-
/>
|
|
1077
|
-
);
|
|
1355
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 4 );
|
|
1356
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'beta' );
|
|
1078
1357
|
|
|
1079
|
-
//
|
|
1080
|
-
|
|
1081
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
|
|
1082
|
-
expect( mockOnSelect ).not.toHaveBeenCalled();
|
|
1358
|
+
// Restore the original implementation of the isRTL function.
|
|
1359
|
+
mockedIsRTL.mockRestore();
|
|
1083
1360
|
} );
|
|
1084
|
-
} );
|
|
1085
|
-
} );
|
|
1086
|
-
|
|
1087
|
-
describe( 'Controlled mode', () => {
|
|
1088
|
-
it( 'should render the tab specified by the `selectedTabId` prop', async () => {
|
|
1089
|
-
await render(
|
|
1090
|
-
<ControlledTabs tabs={ TABS } selectedTabId="beta" />
|
|
1091
|
-
);
|
|
1092
1361
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
await screen.findByRole( 'tabpanel', { name: 'Beta' } )
|
|
1096
|
-
).toBeInTheDocument();
|
|
1097
|
-
} );
|
|
1098
|
-
it( 'should render the specified `selectedTabId`, and ignore the `defaultTabId` prop', async () => {
|
|
1099
|
-
await render(
|
|
1100
|
-
<ControlledTabs
|
|
1101
|
-
tabs={ TABS }
|
|
1102
|
-
selectedTabId="gamma"
|
|
1103
|
-
defaultTabId="beta"
|
|
1104
|
-
/>
|
|
1105
|
-
);
|
|
1106
|
-
|
|
1107
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
|
|
1108
|
-
} );
|
|
1109
|
-
it( 'should not have a selected tab if `selectedTabId` does not match any known tab', async () => {
|
|
1110
|
-
await render(
|
|
1111
|
-
<ControlledTabs
|
|
1112
|
-
tabs={ TABS_WITH_DELTA }
|
|
1113
|
-
selectedTabId="does-not-exist"
|
|
1114
|
-
/>
|
|
1115
|
-
);
|
|
1116
|
-
|
|
1117
|
-
expect(
|
|
1118
|
-
screen.queryByRole( 'tab', { selected: true } )
|
|
1119
|
-
).not.toBeInTheDocument();
|
|
1362
|
+
it( 'should focus tabs in the tablist even if disabled', async () => {
|
|
1363
|
+
const mockOnSelect = jest.fn();
|
|
1120
1364
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
);
|
|
1365
|
+
await render(
|
|
1366
|
+
<Component
|
|
1367
|
+
tabs={ TABS_WITH_BETA_DISABLED }
|
|
1368
|
+
onSelect={ mockOnSelect }
|
|
1369
|
+
/>
|
|
1370
|
+
);
|
|
1128
1371
|
|
|
1129
|
-
|
|
1372
|
+
// Alpha is automatically selected as the selected tab.
|
|
1373
|
+
await waitForComponentToBeInitializedWithSelectedTab( 'Alpha' );
|
|
1130
1374
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
<ControlledTabs
|
|
1134
|
-
tabs={ TABS.filter( ( tab ) => tab.tabId !== 'beta' ) }
|
|
1135
|
-
selectedTabId="beta"
|
|
1136
|
-
/>
|
|
1137
|
-
);
|
|
1138
|
-
|
|
1139
|
-
expect( screen.getAllByRole( 'tab' ) ).toHaveLength( 2 );
|
|
1375
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
1376
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
1140
1377
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1378
|
+
// Focus the tablist (and the selected tab, alpha)
|
|
1379
|
+
// Tab should initially focus the first tab in the tablist, which
|
|
1380
|
+
// is Alpha.
|
|
1381
|
+
await press.Tab();
|
|
1145
1382
|
expect(
|
|
1146
|
-
screen.
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
expect( screen.queryByRole( 'tabpanel' ) ).not.toBeInTheDocument();
|
|
1152
|
-
|
|
1153
|
-
// Restore beta
|
|
1154
|
-
await rerender(
|
|
1155
|
-
<ControlledTabs tabs={ TABS } selectedTabId="beta" />
|
|
1156
|
-
);
|
|
1157
|
-
|
|
1158
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
|
|
1159
|
-
} );
|
|
1160
|
-
|
|
1161
|
-
describe( 'Disabled tab', () => {
|
|
1162
|
-
it( 'should `selectedTabId` refers to a disabled tab', async () => {
|
|
1163
|
-
const TABS_WITH_DELTA_WITH_BETA_DISABLED = TABS_WITH_DELTA.map(
|
|
1164
|
-
( tabObj ) =>
|
|
1165
|
-
tabObj.tabId === 'beta'
|
|
1166
|
-
? {
|
|
1167
|
-
...tabObj,
|
|
1168
|
-
tab: {
|
|
1169
|
-
...tabObj.tab,
|
|
1170
|
-
disabled: true,
|
|
1171
|
-
},
|
|
1172
|
-
}
|
|
1173
|
-
: tabObj
|
|
1174
|
-
);
|
|
1175
|
-
|
|
1176
|
-
await render(
|
|
1177
|
-
<ControlledTabs
|
|
1178
|
-
tabs={ TABS_WITH_DELTA_WITH_BETA_DISABLED }
|
|
1179
|
-
selectedTabId="beta"
|
|
1180
|
-
/>
|
|
1181
|
-
);
|
|
1383
|
+
await screen.findByRole( 'tab', {
|
|
1384
|
+
selected: true,
|
|
1385
|
+
name: 'Alpha',
|
|
1386
|
+
} )
|
|
1387
|
+
).toHaveFocus();
|
|
1182
1388
|
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
const { rerender } = await render(
|
|
1187
|
-
<ControlledTabs tabs={ TABS } selectedTabId="beta" />
|
|
1188
|
-
);
|
|
1389
|
+
// Pressing the right arrow key moves focus to the beta tab, but alpha
|
|
1390
|
+
// remains the selected tab because beta is disabled.
|
|
1391
|
+
await press.ArrowRight();
|
|
1189
1392
|
|
|
1190
|
-
expect(
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1393
|
+
expect(
|
|
1394
|
+
screen.getByRole( 'tab', {
|
|
1395
|
+
selected: false,
|
|
1396
|
+
name: 'Beta',
|
|
1397
|
+
} )
|
|
1398
|
+
).toHaveFocus();
|
|
1399
|
+
expect(
|
|
1400
|
+
screen.getByRole( 'tab', {
|
|
1401
|
+
selected: true,
|
|
1402
|
+
name: 'Alpha',
|
|
1403
|
+
} )
|
|
1404
|
+
).toBeVisible();
|
|
1405
|
+
expect(
|
|
1406
|
+
screen.getByRole( 'tabpanel', {
|
|
1407
|
+
name: 'Alpha',
|
|
1408
|
+
} )
|
|
1409
|
+
).toBeVisible();
|
|
1203
1410
|
|
|
1204
|
-
|
|
1205
|
-
<ControlledTabs
|
|
1206
|
-
tabs={ TABS_WITH_BETA_DISABLED }
|
|
1207
|
-
selectedTabId="beta"
|
|
1208
|
-
/>
|
|
1209
|
-
);
|
|
1411
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
1210
1412
|
|
|
1211
|
-
|
|
1413
|
+
// Press the right arrow key to select the gamma tab
|
|
1414
|
+
await press.ArrowRight();
|
|
1212
1415
|
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1416
|
+
expect(
|
|
1417
|
+
screen.getByRole( 'tab', {
|
|
1418
|
+
selected: true,
|
|
1419
|
+
name: 'Gamma',
|
|
1420
|
+
} )
|
|
1421
|
+
).toHaveFocus();
|
|
1422
|
+
expect(
|
|
1423
|
+
screen.getByRole( 'tabpanel', {
|
|
1424
|
+
name: 'Gamma',
|
|
1425
|
+
} )
|
|
1426
|
+
).toBeVisible();
|
|
1217
1427
|
|
|
1218
|
-
expect(
|
|
1428
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
1429
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'gamma' );
|
|
1219
1430
|
} );
|
|
1220
1431
|
} );
|
|
1221
|
-
|
|
1432
|
+
|
|
1433
|
+
describe( 'When `selectedId` is changed by the controlling component [Controlled]', () => {
|
|
1222
1434
|
describe.each( [ true, false ] )(
|
|
1223
1435
|
'and `selectOnMove` is %s',
|
|
1224
1436
|
( selectOnMove ) => {
|
|
@@ -1231,17 +1443,18 @@ describe( 'Tabs', () => {
|
|
|
1231
1443
|
/>
|
|
1232
1444
|
);
|
|
1233
1445
|
|
|
1234
|
-
|
|
1446
|
+
// Beta is the selected tab.
|
|
1447
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
1235
1448
|
'Beta'
|
|
1236
1449
|
);
|
|
1237
1450
|
|
|
1238
1451
|
// Tab key should focus the currently selected tab, which is Beta.
|
|
1239
1452
|
await press.Tab();
|
|
1240
|
-
expect( await getSelectedTab() ).toHaveTextContent(
|
|
1241
|
-
'Beta'
|
|
1242
|
-
);
|
|
1243
1453
|
expect(
|
|
1244
|
-
screen.getByRole( 'tab', {
|
|
1454
|
+
screen.getByRole( 'tab', {
|
|
1455
|
+
selected: true,
|
|
1456
|
+
name: 'Beta',
|
|
1457
|
+
} )
|
|
1245
1458
|
).toHaveFocus();
|
|
1246
1459
|
|
|
1247
1460
|
await rerender(
|
|
@@ -1253,17 +1466,28 @@ describe( 'Tabs', () => {
|
|
|
1253
1466
|
);
|
|
1254
1467
|
|
|
1255
1468
|
// When the selected tab is changed, focus should not be changed.
|
|
1256
|
-
expect( await getSelectedTab() ).toHaveTextContent(
|
|
1257
|
-
'Gamma'
|
|
1258
|
-
);
|
|
1259
1469
|
expect(
|
|
1260
|
-
screen.getByRole( 'tab', {
|
|
1470
|
+
screen.getByRole( 'tab', {
|
|
1471
|
+
selected: true,
|
|
1472
|
+
name: 'Gamma',
|
|
1473
|
+
} )
|
|
1474
|
+
).toBeVisible();
|
|
1475
|
+
expect(
|
|
1476
|
+
screen.getByRole( 'tab', {
|
|
1477
|
+
selected: false,
|
|
1478
|
+
name: 'Beta',
|
|
1479
|
+
} )
|
|
1261
1480
|
).toHaveFocus();
|
|
1262
1481
|
|
|
1263
|
-
// Arrow
|
|
1264
|
-
|
|
1482
|
+
// Arrow left should move focus to the previous tab (alpha).
|
|
1483
|
+
// The alpha tab should be always focused, and should be selected
|
|
1484
|
+
// when the `selectOnMove` prop is set to `true`.
|
|
1485
|
+
await press.ArrowLeft();
|
|
1265
1486
|
expect(
|
|
1266
|
-
screen.getByRole( 'tab', {
|
|
1487
|
+
screen.getByRole( 'tab', {
|
|
1488
|
+
selected: selectOnMove,
|
|
1489
|
+
name: 'Alpha',
|
|
1490
|
+
} )
|
|
1267
1491
|
).toHaveFocus();
|
|
1268
1492
|
} );
|
|
1269
1493
|
|
|
@@ -1279,20 +1503,22 @@ describe( 'Tabs', () => {
|
|
|
1279
1503
|
</>
|
|
1280
1504
|
);
|
|
1281
1505
|
|
|
1282
|
-
|
|
1506
|
+
// Beta is the selected tab.
|
|
1507
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
1283
1508
|
'Beta'
|
|
1284
1509
|
);
|
|
1285
1510
|
|
|
1286
1511
|
// Tab key should focus the currently selected tab, which is Beta.
|
|
1287
1512
|
await press.Tab();
|
|
1288
1513
|
await press.Tab();
|
|
1289
|
-
expect( await getSelectedTab() ).toHaveTextContent(
|
|
1290
|
-
'Beta'
|
|
1291
|
-
);
|
|
1292
1514
|
expect(
|
|
1293
|
-
screen.getByRole( 'tab', {
|
|
1515
|
+
screen.getByRole( 'tab', {
|
|
1516
|
+
selected: true,
|
|
1517
|
+
name: 'Beta',
|
|
1518
|
+
} )
|
|
1294
1519
|
).toHaveFocus();
|
|
1295
1520
|
|
|
1521
|
+
// Change the selected tab to gamma via a controlled update.
|
|
1296
1522
|
await rerender(
|
|
1297
1523
|
<>
|
|
1298
1524
|
<button>Focus me</button>
|
|
@@ -1305,12 +1531,17 @@ describe( 'Tabs', () => {
|
|
|
1305
1531
|
);
|
|
1306
1532
|
|
|
1307
1533
|
// When the selected tab is changed, it should not automatically receive focus.
|
|
1308
|
-
expect( await getSelectedTab() ).toHaveTextContent(
|
|
1309
|
-
'Gamma'
|
|
1310
|
-
);
|
|
1311
|
-
|
|
1312
1534
|
expect(
|
|
1313
|
-
screen.getByRole( 'tab', {
|
|
1535
|
+
screen.getByRole( 'tab', {
|
|
1536
|
+
selected: true,
|
|
1537
|
+
name: 'Gamma',
|
|
1538
|
+
} )
|
|
1539
|
+
).toBeVisible();
|
|
1540
|
+
expect(
|
|
1541
|
+
screen.getByRole( 'tab', {
|
|
1542
|
+
selected: false,
|
|
1543
|
+
name: 'Beta',
|
|
1544
|
+
} )
|
|
1314
1545
|
).toHaveFocus();
|
|
1315
1546
|
|
|
1316
1547
|
// Press shift+tab, move focus to the button before Tabs
|
|
@@ -1336,125 +1567,439 @@ describe( 'Tabs', () => {
|
|
|
1336
1567
|
}
|
|
1337
1568
|
);
|
|
1338
1569
|
} );
|
|
1570
|
+
} );
|
|
1339
1571
|
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1572
|
+
describe( 'miscellaneous runtime changes', () => {
|
|
1573
|
+
describe( 'removing a tab', () => {
|
|
1574
|
+
describe( 'with no explicitly set initial tab', () => {
|
|
1575
|
+
it( 'should not select a new tab when the selected tab is removed', async () => {
|
|
1576
|
+
const mockOnSelect = jest.fn();
|
|
1345
1577
|
|
|
1346
|
-
|
|
1578
|
+
const { rerender } = await render(
|
|
1579
|
+
<UncontrolledTabs
|
|
1580
|
+
tabs={ TABS }
|
|
1581
|
+
onSelect={ mockOnSelect }
|
|
1582
|
+
/>
|
|
1583
|
+
);
|
|
1347
1584
|
|
|
1348
|
-
|
|
1585
|
+
// Alpha is automatically selected as the selected tab.
|
|
1586
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
1587
|
+
'Alpha'
|
|
1588
|
+
);
|
|
1349
1589
|
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
expect( await getSelectedTab() ).toHaveFocus();
|
|
1590
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
1591
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'alpha' );
|
|
1353
1592
|
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1593
|
+
// Select gamma
|
|
1594
|
+
await click( screen.getByRole( 'tab', { name: 'Gamma' } ) );
|
|
1595
|
+
|
|
1596
|
+
expect(
|
|
1597
|
+
screen.getByRole( 'tab', {
|
|
1598
|
+
selected: true,
|
|
1599
|
+
name: 'Gamma',
|
|
1600
|
+
} )
|
|
1601
|
+
).toHaveFocus();
|
|
1602
|
+
expect(
|
|
1603
|
+
screen.getByRole( 'tabpanel', {
|
|
1604
|
+
name: 'Gamma',
|
|
1605
|
+
} )
|
|
1606
|
+
).toBeVisible();
|
|
1607
|
+
|
|
1608
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
1609
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith( 'gamma' );
|
|
1610
|
+
|
|
1611
|
+
// Remove gamma
|
|
1612
|
+
await rerender(
|
|
1613
|
+
<UncontrolledTabs
|
|
1614
|
+
tabs={ TABS.slice( 0, 2 ) }
|
|
1615
|
+
onSelect={ mockOnSelect }
|
|
1616
|
+
/>
|
|
1617
|
+
);
|
|
1618
|
+
|
|
1619
|
+
expect( screen.getAllByRole( 'tab' ) ).toHaveLength( 2 );
|
|
1620
|
+
|
|
1621
|
+
// No tab should be selected i.e. it doesn't fall back to gamma,
|
|
1622
|
+
// even if it matches the `defaultTabId` prop.
|
|
1623
|
+
expect(
|
|
1624
|
+
screen.queryByRole( 'tab', { selected: true } )
|
|
1625
|
+
).not.toBeInTheDocument();
|
|
1626
|
+
// No tabpanel should be rendered either
|
|
1627
|
+
expect(
|
|
1628
|
+
screen.queryByRole( 'tabpanel' )
|
|
1629
|
+
).not.toBeInTheDocument();
|
|
1630
|
+
|
|
1631
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
1632
|
+
} );
|
|
1358
1633
|
} );
|
|
1634
|
+
|
|
1635
|
+
describe.each( [
|
|
1636
|
+
[ 'defaultTabId', 'Uncontrolled', UncontrolledTabs ],
|
|
1637
|
+
[ 'selectedTabId', 'Controlled', ControlledTabs ],
|
|
1638
|
+
] )(
|
|
1639
|
+
'when using the `%s` prop [%s]',
|
|
1640
|
+
( propName, _mode, Component ) => {
|
|
1641
|
+
it( 'should not select a new tab when the selected tab is removed', async () => {
|
|
1642
|
+
const mockOnSelect = jest.fn();
|
|
1643
|
+
|
|
1644
|
+
const initialComponentProps = {
|
|
1645
|
+
tabs: TABS,
|
|
1646
|
+
[ propName ]: 'gamma',
|
|
1647
|
+
onSelect: mockOnSelect,
|
|
1648
|
+
};
|
|
1649
|
+
|
|
1650
|
+
const { rerender } = await render(
|
|
1651
|
+
<Component { ...initialComponentProps } />
|
|
1652
|
+
);
|
|
1653
|
+
|
|
1654
|
+
// Gamma is the selected tab.
|
|
1655
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
1656
|
+
'Gamma'
|
|
1657
|
+
);
|
|
1658
|
+
|
|
1659
|
+
// Remove gamma
|
|
1660
|
+
await rerender(
|
|
1661
|
+
<Component
|
|
1662
|
+
{ ...initialComponentProps }
|
|
1663
|
+
tabs={ TABS.slice( 0, 2 ) }
|
|
1664
|
+
/>
|
|
1665
|
+
);
|
|
1666
|
+
|
|
1667
|
+
expect( screen.getAllByRole( 'tab' ) ).toHaveLength(
|
|
1668
|
+
2
|
|
1669
|
+
);
|
|
1670
|
+
// No tab should be selected i.e. it doesn't fall back to first tab.
|
|
1671
|
+
expect(
|
|
1672
|
+
screen.queryByRole( 'tab', { selected: true } )
|
|
1673
|
+
).not.toBeInTheDocument();
|
|
1674
|
+
// No tabpanel should be rendered either
|
|
1675
|
+
expect(
|
|
1676
|
+
screen.queryByRole( 'tabpanel' )
|
|
1677
|
+
).not.toBeInTheDocument();
|
|
1678
|
+
|
|
1679
|
+
// Re-add gamma. Gamma becomes selected again.
|
|
1680
|
+
await rerender(
|
|
1681
|
+
<Component { ...initialComponentProps } />
|
|
1682
|
+
);
|
|
1683
|
+
|
|
1684
|
+
expect( screen.getAllByRole( 'tab' ) ).toHaveLength(
|
|
1685
|
+
TABS.length
|
|
1686
|
+
);
|
|
1687
|
+
|
|
1688
|
+
expect(
|
|
1689
|
+
screen.getByRole( 'tab', {
|
|
1690
|
+
selected: true,
|
|
1691
|
+
name: 'Gamma',
|
|
1692
|
+
} )
|
|
1693
|
+
).toBeVisible();
|
|
1694
|
+
expect(
|
|
1695
|
+
screen.getByRole( 'tabpanel', {
|
|
1696
|
+
name: 'Gamma',
|
|
1697
|
+
} )
|
|
1698
|
+
).toBeVisible();
|
|
1699
|
+
|
|
1700
|
+
expect( mockOnSelect ).not.toHaveBeenCalled();
|
|
1701
|
+
} );
|
|
1702
|
+
|
|
1703
|
+
it( `should not select the tab matching the \`${ propName }\` prop as a fallback when the selected tab is removed`, async () => {
|
|
1704
|
+
const mockOnSelect = jest.fn();
|
|
1705
|
+
|
|
1706
|
+
const initialComponentProps = {
|
|
1707
|
+
tabs: TABS,
|
|
1708
|
+
[ propName ]: 'gamma',
|
|
1709
|
+
onSelect: mockOnSelect,
|
|
1710
|
+
};
|
|
1711
|
+
|
|
1712
|
+
const { rerender } = await render(
|
|
1713
|
+
<Component { ...initialComponentProps } />
|
|
1714
|
+
);
|
|
1715
|
+
|
|
1716
|
+
// Gamma is the selected tab.
|
|
1717
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
1718
|
+
'Gamma'
|
|
1719
|
+
);
|
|
1720
|
+
|
|
1721
|
+
// Select alpha
|
|
1722
|
+
await click(
|
|
1723
|
+
screen.getByRole( 'tab', { name: 'Alpha' } )
|
|
1724
|
+
);
|
|
1725
|
+
|
|
1726
|
+
expect(
|
|
1727
|
+
screen.getByRole( 'tab', {
|
|
1728
|
+
selected: true,
|
|
1729
|
+
name: 'Alpha',
|
|
1730
|
+
} )
|
|
1731
|
+
).toHaveFocus();
|
|
1732
|
+
expect(
|
|
1733
|
+
screen.getByRole( 'tabpanel', {
|
|
1734
|
+
name: 'Alpha',
|
|
1735
|
+
} )
|
|
1736
|
+
).toBeVisible();
|
|
1737
|
+
|
|
1738
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
1739
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith(
|
|
1740
|
+
'alpha'
|
|
1741
|
+
);
|
|
1742
|
+
|
|
1743
|
+
// Remove alpha
|
|
1744
|
+
await rerender(
|
|
1745
|
+
<Component
|
|
1746
|
+
{ ...initialComponentProps }
|
|
1747
|
+
tabs={ TABS.slice( 1 ) }
|
|
1748
|
+
/>
|
|
1749
|
+
);
|
|
1750
|
+
|
|
1751
|
+
expect( screen.getAllByRole( 'tab' ) ).toHaveLength(
|
|
1752
|
+
2
|
|
1753
|
+
);
|
|
1754
|
+
|
|
1755
|
+
// No tab should be selected i.e. it doesn't fall back to gamma,
|
|
1756
|
+
// even if it matches the `defaultTabId` prop.
|
|
1757
|
+
expect(
|
|
1758
|
+
screen.queryByRole( 'tab', { selected: true } )
|
|
1759
|
+
).not.toBeInTheDocument();
|
|
1760
|
+
// No tabpanel should be rendered either
|
|
1761
|
+
expect(
|
|
1762
|
+
screen.queryByRole( 'tabpanel' )
|
|
1763
|
+
).not.toBeInTheDocument();
|
|
1764
|
+
|
|
1765
|
+
// Re-add alpha. Alpha becomes selected again.
|
|
1766
|
+
await rerender(
|
|
1767
|
+
<Component { ...initialComponentProps } />
|
|
1768
|
+
);
|
|
1769
|
+
|
|
1770
|
+
expect( screen.getAllByRole( 'tab' ) ).toHaveLength(
|
|
1771
|
+
TABS.length
|
|
1772
|
+
);
|
|
1773
|
+
|
|
1774
|
+
expect(
|
|
1775
|
+
screen.getByRole( 'tab', {
|
|
1776
|
+
selected: true,
|
|
1777
|
+
name: 'Alpha',
|
|
1778
|
+
} )
|
|
1779
|
+
).toBeVisible();
|
|
1780
|
+
expect(
|
|
1781
|
+
screen.getByRole( 'tabpanel', {
|
|
1782
|
+
name: 'Alpha',
|
|
1783
|
+
} )
|
|
1784
|
+
).toBeVisible();
|
|
1785
|
+
|
|
1786
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
1787
|
+
} );
|
|
1788
|
+
}
|
|
1789
|
+
);
|
|
1359
1790
|
} );
|
|
1360
|
-
describe( 'When `selectOnMove` is `false`', () => {
|
|
1361
|
-
it( 'should apply focus without automatically changing the selected tab', async () => {
|
|
1362
|
-
await render(
|
|
1363
|
-
<ControlledTabs
|
|
1364
|
-
tabs={ TABS }
|
|
1365
|
-
selectedTabId="beta"
|
|
1366
|
-
selectOnMove={ false }
|
|
1367
|
-
/>
|
|
1368
|
-
);
|
|
1369
1791
|
|
|
1370
|
-
|
|
1792
|
+
describe( 'adding a tab', () => {
|
|
1793
|
+
describe.each( [
|
|
1794
|
+
[ 'defaultTabId', 'Uncontrolled', UncontrolledTabs ],
|
|
1795
|
+
[ 'selectedTabId', 'Controlled', ControlledTabs ],
|
|
1796
|
+
] )(
|
|
1797
|
+
'when using the `%s` prop [%s]',
|
|
1798
|
+
( propName, _mode, Component ) => {
|
|
1799
|
+
it( `should select a newly added tab if it matches the \`${ propName }\` prop`, async () => {
|
|
1800
|
+
const mockOnSelect = jest.fn();
|
|
1801
|
+
|
|
1802
|
+
const initialComponentProps = {
|
|
1803
|
+
tabs: TABS,
|
|
1804
|
+
[ propName ]: 'delta',
|
|
1805
|
+
onSelect: mockOnSelect,
|
|
1806
|
+
};
|
|
1371
1807
|
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
expect(
|
|
1376
|
-
await screen.findByRole( 'tab', { name: 'Beta' } )
|
|
1377
|
-
).toHaveFocus()
|
|
1378
|
-
);
|
|
1808
|
+
const { rerender } = await render(
|
|
1809
|
+
<Component { ...initialComponentProps } />
|
|
1810
|
+
);
|
|
1379
1811
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Beta' );
|
|
1812
|
+
// No initially selected tabs or tabpanels, since the `defaultTabId`
|
|
1813
|
+
// prop is not matching any known tabs.
|
|
1814
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
1815
|
+
undefined
|
|
1816
|
+
);
|
|
1386
1817
|
|
|
1387
|
-
|
|
1388
|
-
await press.Space();
|
|
1389
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
|
|
1818
|
+
expect( mockOnSelect ).not.toHaveBeenCalled();
|
|
1390
1819
|
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1820
|
+
// Re-render with beta disabled.
|
|
1821
|
+
await rerender(
|
|
1822
|
+
<Component
|
|
1823
|
+
{ ...initialComponentProps }
|
|
1824
|
+
tabs={ TABS_WITH_DELTA }
|
|
1825
|
+
/>
|
|
1826
|
+
);
|
|
1397
1827
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1828
|
+
// Delta becomes selected
|
|
1829
|
+
expect(
|
|
1830
|
+
screen.getByRole( 'tab', {
|
|
1831
|
+
selected: true,
|
|
1832
|
+
name: 'Delta',
|
|
1833
|
+
} )
|
|
1834
|
+
).toBeVisible();
|
|
1835
|
+
expect(
|
|
1836
|
+
screen.getByRole( 'tabpanel', {
|
|
1837
|
+
name: 'Delta',
|
|
1838
|
+
} )
|
|
1839
|
+
).toBeVisible();
|
|
1840
|
+
|
|
1841
|
+
expect( mockOnSelect ).not.toHaveBeenCalled();
|
|
1842
|
+
} );
|
|
1843
|
+
}
|
|
1844
|
+
);
|
|
1402
1845
|
} );
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
{
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
</Tabs.Tab>
|
|
1419
|
-
) ) }
|
|
1420
|
-
</Tabs.TabList>
|
|
1421
|
-
{ TABS_WITH_DELTA_REVERSED.map( ( tabObj ) => (
|
|
1422
|
-
<Tabs.TabPanel
|
|
1423
|
-
key={ tabObj.tabId }
|
|
1424
|
-
tabId={ tabObj.tabId }
|
|
1425
|
-
focusable={ tabObj.tabpanel?.focusable }
|
|
1426
|
-
>
|
|
1427
|
-
{ tabObj.content }
|
|
1428
|
-
</Tabs.TabPanel>
|
|
1429
|
-
) ) }
|
|
1430
|
-
</Tabs>
|
|
1431
|
-
);
|
|
1846
|
+
describe( 'a tab becomes disabled', () => {
|
|
1847
|
+
describe.each( [
|
|
1848
|
+
[ 'defaultTabId', 'Uncontrolled', UncontrolledTabs ],
|
|
1849
|
+
[ 'selectedTabId', 'Controlled', ControlledTabs ],
|
|
1850
|
+
] )(
|
|
1851
|
+
'when using the `%s` prop [%s]',
|
|
1852
|
+
( propName, _mode, Component ) => {
|
|
1853
|
+
it( `should keep the initial tab matching the \`${ propName }\` prop as selected even if it becomes disabled`, async () => {
|
|
1854
|
+
const mockOnSelect = jest.fn();
|
|
1855
|
+
|
|
1856
|
+
const initialComponentProps = {
|
|
1857
|
+
tabs: TABS,
|
|
1858
|
+
[ propName ]: 'beta',
|
|
1859
|
+
onSelect: mockOnSelect,
|
|
1860
|
+
};
|
|
1432
1861
|
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
'Selected tab: Alpha'
|
|
1437
|
-
);
|
|
1862
|
+
const { rerender } = await render(
|
|
1863
|
+
<Component { ...initialComponentProps } />
|
|
1864
|
+
);
|
|
1438
1865
|
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
'Selected tab: Beta'
|
|
1444
|
-
);
|
|
1866
|
+
// Beta is the selected tab.
|
|
1867
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
1868
|
+
'Beta'
|
|
1869
|
+
);
|
|
1445
1870
|
|
|
1446
|
-
|
|
1447
|
-
await click( screen.getByRole( 'tab', { name: 'Gamma' } ) );
|
|
1448
|
-
expect( await getSelectedTab() ).toHaveTextContent( 'Gamma' );
|
|
1449
|
-
expect( screen.getByRole( 'tabpanel' ) ).toHaveTextContent(
|
|
1450
|
-
'Selected tab: Gamma'
|
|
1451
|
-
);
|
|
1871
|
+
expect( mockOnSelect ).not.toHaveBeenCalled();
|
|
1452
1872
|
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1873
|
+
// Re-render with beta disabled.
|
|
1874
|
+
await rerender(
|
|
1875
|
+
<Component
|
|
1876
|
+
{ ...initialComponentProps }
|
|
1877
|
+
tabs={ TABS_WITH_BETA_DISABLED }
|
|
1878
|
+
/>
|
|
1879
|
+
);
|
|
1880
|
+
|
|
1881
|
+
// Beta continues to be selected and focused, even if it is disabled.
|
|
1882
|
+
expect(
|
|
1883
|
+
screen.getByRole( 'tab', {
|
|
1884
|
+
selected: true,
|
|
1885
|
+
name: 'Beta',
|
|
1886
|
+
} )
|
|
1887
|
+
).toBeVisible();
|
|
1888
|
+
expect(
|
|
1889
|
+
screen.getByRole( 'tabpanel', {
|
|
1890
|
+
name: 'Beta',
|
|
1891
|
+
} )
|
|
1892
|
+
).toBeVisible();
|
|
1893
|
+
|
|
1894
|
+
// Re-enable beta.
|
|
1895
|
+
await rerender(
|
|
1896
|
+
<Component { ...initialComponentProps } />
|
|
1897
|
+
);
|
|
1898
|
+
|
|
1899
|
+
// Beta continues to be selected and focused.
|
|
1900
|
+
expect(
|
|
1901
|
+
screen.getByRole( 'tab', {
|
|
1902
|
+
selected: true,
|
|
1903
|
+
name: 'Beta',
|
|
1904
|
+
} )
|
|
1905
|
+
).toBeVisible();
|
|
1906
|
+
expect(
|
|
1907
|
+
screen.getByRole( 'tabpanel', {
|
|
1908
|
+
name: 'Beta',
|
|
1909
|
+
} )
|
|
1910
|
+
).toBeVisible();
|
|
1911
|
+
|
|
1912
|
+
expect( mockOnSelect ).not.toHaveBeenCalled();
|
|
1913
|
+
} );
|
|
1914
|
+
|
|
1915
|
+
it( 'should keep the current tab selected by the user as selected even if it becomes disabled', async () => {
|
|
1916
|
+
const mockOnSelect = jest.fn();
|
|
1917
|
+
|
|
1918
|
+
const { rerender } = await render(
|
|
1919
|
+
<Component
|
|
1920
|
+
tabs={ TABS }
|
|
1921
|
+
onSelect={ mockOnSelect }
|
|
1922
|
+
/>
|
|
1923
|
+
);
|
|
1924
|
+
|
|
1925
|
+
// Alpha is automatically selected as the selected tab.
|
|
1926
|
+
await waitForComponentToBeInitializedWithSelectedTab(
|
|
1927
|
+
'Alpha'
|
|
1928
|
+
);
|
|
1929
|
+
|
|
1930
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 1 );
|
|
1931
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith(
|
|
1932
|
+
'alpha'
|
|
1933
|
+
);
|
|
1934
|
+
|
|
1935
|
+
// Click on beta tab, beta becomes selected.
|
|
1936
|
+
await click(
|
|
1937
|
+
screen.getByRole( 'tab', { name: 'Beta' } )
|
|
1938
|
+
);
|
|
1939
|
+
|
|
1940
|
+
expect(
|
|
1941
|
+
screen.getByRole( 'tab', {
|
|
1942
|
+
selected: true,
|
|
1943
|
+
name: 'Beta',
|
|
1944
|
+
} )
|
|
1945
|
+
).toBeVisible();
|
|
1946
|
+
expect(
|
|
1947
|
+
screen.getByRole( 'tabpanel', {
|
|
1948
|
+
name: 'Beta',
|
|
1949
|
+
} )
|
|
1950
|
+
).toBeVisible();
|
|
1951
|
+
|
|
1952
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
1953
|
+
expect( mockOnSelect ).toHaveBeenLastCalledWith(
|
|
1954
|
+
'beta'
|
|
1955
|
+
);
|
|
1956
|
+
|
|
1957
|
+
// Re-render with beta disabled.
|
|
1958
|
+
await rerender(
|
|
1959
|
+
<Component
|
|
1960
|
+
tabs={ TABS_WITH_BETA_DISABLED }
|
|
1961
|
+
onSelect={ mockOnSelect }
|
|
1962
|
+
/>
|
|
1963
|
+
);
|
|
1964
|
+
|
|
1965
|
+
// Beta continues to be selected, even if it is disabled.
|
|
1966
|
+
expect(
|
|
1967
|
+
screen.getByRole( 'tab', {
|
|
1968
|
+
selected: true,
|
|
1969
|
+
name: 'Beta',
|
|
1970
|
+
} )
|
|
1971
|
+
).toHaveFocus();
|
|
1972
|
+
expect(
|
|
1973
|
+
screen.getByRole( 'tabpanel', {
|
|
1974
|
+
name: 'Beta',
|
|
1975
|
+
} )
|
|
1976
|
+
).toBeVisible();
|
|
1977
|
+
|
|
1978
|
+
// Re-enable beta.
|
|
1979
|
+
await rerender(
|
|
1980
|
+
<Component
|
|
1981
|
+
tabs={ TABS }
|
|
1982
|
+
onSelect={ mockOnSelect }
|
|
1983
|
+
/>
|
|
1984
|
+
);
|
|
1985
|
+
|
|
1986
|
+
// Beta continues to be selected and focused.
|
|
1987
|
+
expect(
|
|
1988
|
+
screen.getByRole( 'tab', {
|
|
1989
|
+
selected: true,
|
|
1990
|
+
name: 'Beta',
|
|
1991
|
+
} )
|
|
1992
|
+
).toBeVisible();
|
|
1993
|
+
expect(
|
|
1994
|
+
screen.getByRole( 'tabpanel', {
|
|
1995
|
+
name: 'Beta',
|
|
1996
|
+
} )
|
|
1997
|
+
).toBeVisible();
|
|
1998
|
+
|
|
1999
|
+
expect( mockOnSelect ).toHaveBeenCalledTimes( 2 );
|
|
2000
|
+
} );
|
|
2001
|
+
}
|
|
2002
|
+
);
|
|
2003
|
+
} );
|
|
1459
2004
|
} );
|
|
1460
2005
|
} );
|