@wordpress/components 27.0.0 → 27.1.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 +22 -0
- package/build/base-control/index.native.js.map +1 -1
- package/build/border-box-control/border-box-control/component.js.map +1 -1
- package/build/border-box-control/border-box-control-split-controls/component.js.map +1 -1
- package/build/border-control/border-control-dropdown/component.js.map +1 -1
- package/build/button/index.js +2 -2
- package/build/button/index.js.map +1 -1
- package/build/button/index.native.js.map +1 -1
- package/build/confirm-dialog/component.js.map +1 -1
- package/build/custom-select-control-v2/default-component/index.js.map +1 -1
- package/build/custom-select-control-v2/index.js +2 -2
- package/build/custom-select-control-v2/index.js.map +1 -1
- package/build/custom-select-control-v2/types.js.map +1 -1
- package/build/draggable/index.native.js +2 -2
- package/build/draggable/index.native.js.map +1 -1
- package/build/dropdown-menu/index.native.js.map +1 -1
- package/build/flex/flex/hook.js +1 -1
- package/build/flex/flex/hook.js.map +1 -1
- package/build/font-size-picker/index.native.js.map +1 -1
- package/build/form-token-field/index.js.map +1 -1
- package/build/h-stack/hook.js +6 -1
- package/build/h-stack/hook.js.map +1 -1
- package/build/mobile/bottom-sheet/button.native.js.map +1 -1
- package/build/mobile/bottom-sheet/index.native.js.map +1 -1
- package/build/mobile/bottom-sheet/range-cell.native.js.map +1 -1
- package/build/mobile/bottom-sheet/stepper-cell/index.native.js.map +1 -1
- package/build/mobile/bottom-sheet-select-control/index.native.js.map +1 -1
- package/build/mobile/bottom-sheet-text-control/index.native.js.map +1 -1
- package/build/mobile/gradient/index.native.js.map +1 -1
- package/build/mobile/image/index.native.js +4 -13
- package/build/mobile/image/index.native.js.map +1 -1
- package/build/mobile/media-edit/index.native.js.map +1 -1
- package/build/palette-edit/index.js.map +1 -1
- package/build/query-controls/index.native.js.map +1 -1
- package/build/range-control/index.js.map +1 -1
- package/build/search-control/index.native.js.map +1 -1
- package/build/snackbar/index.js +3 -2
- package/build/snackbar/index.js.map +1 -1
- package/build/snackbar/list.js +2 -1
- package/build/snackbar/list.js.map +1 -1
- package/build/snackbar/types.js.map +1 -1
- package/build/tabs/index.js +7 -7
- package/build/tabs/index.js.map +1 -1
- package/build/tabs/types.js.map +1 -1
- package/build/utils/hooks/index.js +0 -7
- package/build/utils/hooks/index.js.map +1 -1
- package/build-module/base-control/index.native.js.map +1 -1
- package/build-module/border-box-control/border-box-control/component.js.map +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-dropdown/component.js.map +1 -1
- package/build-module/button/index.js +2 -2
- package/build-module/button/index.js.map +1 -1
- package/build-module/button/index.native.js.map +1 -1
- package/build-module/confirm-dialog/component.js.map +1 -1
- package/build-module/custom-select-control-v2/default-component/index.js.map +1 -1
- package/build-module/custom-select-control-v2/index.js +1 -1
- package/build-module/custom-select-control-v2/index.js.map +1 -1
- package/build-module/custom-select-control-v2/types.js.map +1 -1
- package/build-module/draggable/index.native.js +2 -2
- package/build-module/draggable/index.native.js.map +1 -1
- package/build-module/dropdown-menu/index.native.js.map +1 -1
- package/build-module/flex/flex/hook.js +1 -1
- package/build-module/flex/flex/hook.js.map +1 -1
- package/build-module/font-size-picker/index.native.js.map +1 -1
- package/build-module/form-token-field/index.js.map +1 -1
- package/build-module/h-stack/hook.js +6 -1
- package/build-module/h-stack/hook.js.map +1 -1
- package/build-module/mobile/bottom-sheet/button.native.js.map +1 -1
- package/build-module/mobile/bottom-sheet/index.native.js.map +1 -1
- package/build-module/mobile/bottom-sheet/range-cell.native.js.map +1 -1
- package/build-module/mobile/bottom-sheet/stepper-cell/index.native.js.map +1 -1
- package/build-module/mobile/bottom-sheet-select-control/index.native.js.map +1 -1
- package/build-module/mobile/bottom-sheet-text-control/index.native.js.map +1 -1
- package/build-module/mobile/gradient/index.native.js.map +1 -1
- package/build-module/mobile/image/index.native.js +6 -15
- package/build-module/mobile/image/index.native.js.map +1 -1
- package/build-module/mobile/media-edit/index.native.js.map +1 -1
- package/build-module/palette-edit/index.js.map +1 -1
- package/build-module/query-controls/index.native.js.map +1 -1
- package/build-module/range-control/index.js.map +1 -1
- package/build-module/search-control/index.native.js.map +1 -1
- package/build-module/snackbar/index.js +3 -2
- package/build-module/snackbar/index.js.map +1 -1
- package/build-module/snackbar/list.js +2 -1
- package/build-module/snackbar/list.js.map +1 -1
- package/build-module/snackbar/types.js.map +1 -1
- package/build-module/tabs/index.js +7 -7
- package/build-module/tabs/index.js.map +1 -1
- package/build-module/tabs/types.js.map +1 -1
- package/build-module/utils/hooks/index.js +0 -1
- package/build-module/utils/hooks/index.js.map +1 -1
- package/build-style/style-rtl.css +8 -1
- package/build-style/style.css +8 -1
- package/build-types/custom-select-control-v2/default-component/index.d.ts +2 -1
- package/build-types/custom-select-control-v2/default-component/index.d.ts.map +1 -1
- package/build-types/custom-select-control-v2/index.d.ts +1 -1
- package/build-types/custom-select-control-v2/index.d.ts.map +1 -1
- package/build-types/custom-select-control-v2/legacy-component/test/index.d.ts +2 -0
- package/build-types/custom-select-control-v2/legacy-component/test/index.d.ts.map +1 -0
- package/build-types/custom-select-control-v2/stories/default.story.d.ts +4 -3
- package/build-types/custom-select-control-v2/stories/default.story.d.ts.map +1 -1
- package/build-types/custom-select-control-v2/stories/legacy.story.d.ts +2 -2
- package/build-types/custom-select-control-v2/stories/legacy.story.d.ts.map +1 -1
- package/build-types/custom-select-control-v2/types.d.ts +0 -1
- package/build-types/custom-select-control-v2/types.d.ts.map +1 -1
- package/build-types/flex/flex/hook.d.ts +2 -3
- package/build-types/flex/flex/hook.d.ts.map +1 -1
- package/build-types/h-stack/hook.d.ts +2 -4
- package/build-types/h-stack/hook.d.ts.map +1 -1
- package/build-types/navigation/stories/utils/hide-if-empty.d.ts.map +1 -1
- package/build-types/radio-group/stories/index.story.d.ts.map +1 -1
- package/build-types/snackbar/index.d.ts +5 -2
- package/build-types/snackbar/index.d.ts.map +1 -1
- package/build-types/snackbar/list.d.ts.map +1 -1
- package/build-types/snackbar/test/index.d.ts +2 -0
- package/build-types/snackbar/test/index.d.ts.map +1 -0
- package/build-types/snackbar/test/list.d.ts +2 -0
- package/build-types/snackbar/test/list.d.ts.map +1 -0
- package/build-types/snackbar/types.d.ts +18 -2
- package/build-types/snackbar/types.d.ts.map +1 -1
- package/build-types/tabs/index.d.ts +1 -1
- package/build-types/tabs/types.d.ts +1 -1
- package/build-types/utils/hooks/index.d.ts +0 -1
- package/build-types/v-stack/hook.d.ts +2 -4
- package/build-types/v-stack/hook.d.ts.map +1 -1
- package/package.json +19 -19
- package/src/base-control/index.native.js +1 -1
- package/src/base-control/test/index.tsx +1 -1
- package/src/border-box-control/border-box-control/component.tsx +1 -1
- package/src/border-box-control/border-box-control-split-controls/component.tsx +4 -4
- package/src/border-control/border-control-dropdown/component.tsx +1 -1
- package/src/button/index.native.js +1 -1
- package/src/button/index.tsx +1 -1
- package/src/button/style.scss +1 -3
- package/src/circular-option-picker/test/index.tsx +2 -4
- package/src/combobox-control/test/index.tsx +1 -1
- package/src/confirm-dialog/component.tsx +1 -1
- package/src/confirm-dialog/test/index.tsx +5 -21
- package/src/custom-select-control-v2/default-component/index.tsx +4 -1
- package/src/custom-select-control-v2/index.tsx +1 -1
- package/src/custom-select-control-v2/legacy-component/test/index.tsx +457 -0
- package/src/custom-select-control-v2/stories/legacy.story.tsx +5 -6
- package/src/custom-select-control-v2/test/index.tsx +279 -749
- package/src/custom-select-control-v2/types.ts +0 -1
- package/src/disabled/test/index.tsx +1 -1
- package/src/draggable/index.native.js +2 -2
- package/src/draggable/test/index.native.js +6 -2
- package/src/dropdown-menu/index.native.js +2 -2
- package/src/flex/flex/hook.ts +1 -1
- package/src/font-size-picker/index.native.js +2 -2
- package/src/form-token-field/index.tsx +1 -1
- package/src/h-stack/hook.tsx +2 -1
- package/src/h-stack/test/index.tsx +10 -0
- package/src/item-group/test/index.js +2 -2
- package/src/mobile/bottom-sheet/bottom-sheet-navigation/test/navigation-container.native.js +10 -15
- package/src/mobile/bottom-sheet/button.native.js +1 -5
- package/src/mobile/bottom-sheet/index.native.js +2 -2
- package/src/mobile/bottom-sheet/range-cell.native.js +1 -1
- package/src/mobile/bottom-sheet/stepper-cell/index.native.js +2 -2
- package/src/mobile/bottom-sheet-select-control/index.native.js +1 -1
- package/src/mobile/bottom-sheet-text-control/index.native.js +1 -1
- package/src/mobile/gradient/index.native.js +1 -1
- package/src/mobile/image/index.native.js +8 -23
- package/src/mobile/media-edit/index.native.js +1 -1
- package/src/modal/test/index.tsx +1 -1
- package/src/navigation/stories/utils/hide-if-empty.tsx +2 -6
- package/src/palette-edit/index.tsx +2 -2
- package/src/popover/test/index.tsx +1 -4
- package/src/query-controls/index.native.js +2 -2
- package/src/radio-group/stories/index.story.tsx +1 -0
- package/src/range-control/index.tsx +3 -3
- package/src/range-control/test/index.tsx +2 -2
- package/src/search-control/index.native.js +1 -1
- package/src/snackbar/index.tsx +5 -2
- package/src/snackbar/list.tsx +6 -1
- package/src/snackbar/stories/list.story.tsx +0 -3
- package/src/snackbar/test/index.tsx +267 -0
- package/src/snackbar/test/list.tsx +46 -0
- package/src/snackbar/types.ts +31 -3
- package/src/tabs/README.md +18 -18
- package/src/tabs/index.tsx +7 -7
- package/src/tabs/stories/index.story.tsx +1 -1
- package/src/tabs/test/index.tsx +30 -30
- package/src/tabs/types.ts +1 -1
- package/src/toggle-group-control/test/index.tsx +1 -1
- package/src/tools-panel/stories/index.story.tsx +8 -8
- package/src/tools-panel/test/index.tsx +10 -28
- package/src/tooltip/style.scss +2 -1
- package/src/tooltip/test/index.native.js +3 -3
- package/src/tree-grid/test/index.tsx +1 -1
- package/src/utils/hooks/index.js +0 -1
- package/src/v-stack/test/index.tsx +10 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/build/custom-select-control-v2/legacy-adapter.js +0 -29
- package/build/custom-select-control-v2/legacy-adapter.js.map +0 -1
- package/build/utils/hooks/use-latest-ref.js +0 -33
- package/build/utils/hooks/use-latest-ref.js.map +0 -1
- package/build-module/custom-select-control-v2/legacy-adapter.js +0 -21
- package/build-module/custom-select-control-v2/legacy-adapter.js.map +0 -1
- package/build-module/utils/hooks/use-latest-ref.js +0 -27
- package/build-module/utils/hooks/use-latest-ref.js.map +0 -1
- package/build-types/custom-select-control-v2/legacy-adapter.d.ts +0 -6
- package/build-types/custom-select-control-v2/legacy-adapter.d.ts.map +0 -1
- package/build-types/utils/hooks/use-latest-ref.d.ts +0 -15
- package/build-types/utils/hooks/use-latest-ref.d.ts.map +0 -1
- package/src/custom-select-control-v2/legacy-adapter.tsx +0 -25
- package/src/utils/hooks/test/use-latest-ref.js +0 -119
- package/src/utils/hooks/use-latest-ref.ts +0 -29
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { render, screen, within } from '@testing-library/react';
|
|
5
|
+
import { click } from '@ariakit/test';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* WordPress dependencies
|
|
9
|
+
*/
|
|
10
|
+
import { speak } from '@wordpress/a11y';
|
|
11
|
+
import { SVG, Path } from '@wordpress/primitives';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Internal dependencies
|
|
15
|
+
*/
|
|
16
|
+
import Snackbar from '../index';
|
|
17
|
+
|
|
18
|
+
jest.mock( '@wordpress/a11y', () => ( { speak: jest.fn() } ) );
|
|
19
|
+
const mockedSpeak = jest.mocked( speak );
|
|
20
|
+
|
|
21
|
+
describe( 'Snackbar', () => {
|
|
22
|
+
const testId = 'snackbar';
|
|
23
|
+
|
|
24
|
+
beforeEach( () => {
|
|
25
|
+
mockedSpeak.mockReset();
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
it( 'should render correctly', () => {
|
|
29
|
+
render( <Snackbar>Message</Snackbar> );
|
|
30
|
+
|
|
31
|
+
const snackbar = screen.getByTestId( testId );
|
|
32
|
+
|
|
33
|
+
expect( snackbar ).toBeVisible();
|
|
34
|
+
expect( snackbar ).toHaveTextContent( 'Message' );
|
|
35
|
+
} );
|
|
36
|
+
|
|
37
|
+
it( 'should render with an additional className', () => {
|
|
38
|
+
render( <Snackbar className="gutenberg">Message</Snackbar> );
|
|
39
|
+
|
|
40
|
+
expect( screen.getByTestId( testId ) ).toHaveClass( 'gutenberg' );
|
|
41
|
+
} );
|
|
42
|
+
|
|
43
|
+
it( 'should render with an icon', () => {
|
|
44
|
+
const testIcon = (
|
|
45
|
+
<SVG data-testid="icon">
|
|
46
|
+
<Path />
|
|
47
|
+
</SVG>
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
render( <Snackbar icon={ testIcon }>Message</Snackbar> );
|
|
51
|
+
|
|
52
|
+
const snackbar = screen.getByTestId( testId );
|
|
53
|
+
const icon = within( snackbar ).getByTestId( 'icon' );
|
|
54
|
+
|
|
55
|
+
expect( icon ).toBeVisible();
|
|
56
|
+
} );
|
|
57
|
+
|
|
58
|
+
it( 'should be dismissible by clicking the snackbar', async () => {
|
|
59
|
+
const onRemove = jest.fn();
|
|
60
|
+
const onDismiss = jest.fn();
|
|
61
|
+
|
|
62
|
+
render(
|
|
63
|
+
<Snackbar onRemove={ onRemove } onDismiss={ onDismiss }>
|
|
64
|
+
Message
|
|
65
|
+
</Snackbar>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const snackbar = screen.getByTestId( testId );
|
|
69
|
+
|
|
70
|
+
expect( snackbar ).toHaveAttribute( 'role', 'button' );
|
|
71
|
+
expect( snackbar ).toHaveAttribute(
|
|
72
|
+
'aria-label',
|
|
73
|
+
'Dismiss this notice'
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
await click( snackbar );
|
|
77
|
+
|
|
78
|
+
expect( onRemove ).toHaveBeenCalledTimes( 1 );
|
|
79
|
+
expect( onDismiss ).toHaveBeenCalledTimes( 1 );
|
|
80
|
+
} );
|
|
81
|
+
|
|
82
|
+
it( 'should not be dismissible by clicking the snackbar when the `explicitDismiss` prop is set to `true`', async () => {
|
|
83
|
+
const onRemove = jest.fn();
|
|
84
|
+
const onDismiss = jest.fn();
|
|
85
|
+
|
|
86
|
+
render(
|
|
87
|
+
<Snackbar
|
|
88
|
+
explicitDismiss
|
|
89
|
+
onRemove={ onRemove }
|
|
90
|
+
onDismiss={ onDismiss }
|
|
91
|
+
>
|
|
92
|
+
Message
|
|
93
|
+
</Snackbar>
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const snackbar = screen.getByTestId( testId );
|
|
97
|
+
|
|
98
|
+
expect( snackbar ).not.toHaveAttribute( 'role', 'button' );
|
|
99
|
+
expect( snackbar ).not.toHaveAttribute(
|
|
100
|
+
'aria-label',
|
|
101
|
+
'Dismiss this notice'
|
|
102
|
+
);
|
|
103
|
+
expect( snackbar ).toHaveClass(
|
|
104
|
+
'components-snackbar-explicit-dismiss'
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
await click( snackbar );
|
|
108
|
+
|
|
109
|
+
expect( onRemove ).not.toHaveBeenCalled();
|
|
110
|
+
expect( onDismiss ).not.toHaveBeenCalled();
|
|
111
|
+
} );
|
|
112
|
+
|
|
113
|
+
it( 'should be dismissible by clicking the close button when the `explicitDismiss` prop is set to `true`', async () => {
|
|
114
|
+
const onRemove = jest.fn();
|
|
115
|
+
const onDismiss = jest.fn();
|
|
116
|
+
|
|
117
|
+
render(
|
|
118
|
+
<Snackbar
|
|
119
|
+
explicitDismiss
|
|
120
|
+
onRemove={ onRemove }
|
|
121
|
+
onDismiss={ onDismiss }
|
|
122
|
+
>
|
|
123
|
+
Message
|
|
124
|
+
</Snackbar>
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const snackbar = screen.getByTestId( testId );
|
|
128
|
+
const closeButton = within( snackbar ).getByRole( 'button', {
|
|
129
|
+
name: 'Dismiss this notice',
|
|
130
|
+
} );
|
|
131
|
+
|
|
132
|
+
await click( closeButton );
|
|
133
|
+
|
|
134
|
+
expect( onRemove ).toHaveBeenCalledTimes( 1 );
|
|
135
|
+
expect( onDismiss ).toHaveBeenCalledTimes( 1 );
|
|
136
|
+
} );
|
|
137
|
+
|
|
138
|
+
describe( 'actions', () => {
|
|
139
|
+
it( 'should render only the first action with a warning when multiple actions are passed', () => {
|
|
140
|
+
render(
|
|
141
|
+
<Snackbar
|
|
142
|
+
actions={ [
|
|
143
|
+
{ label: 'One', url: 'https://example.com' },
|
|
144
|
+
{ label: 'Two', url: 'https://example.com' },
|
|
145
|
+
{ label: 'Three', url: 'https://example.com' },
|
|
146
|
+
] }
|
|
147
|
+
>
|
|
148
|
+
Message
|
|
149
|
+
</Snackbar>
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
expect( console ).toHaveWarnedWith(
|
|
153
|
+
'Snackbar can only have one action. Use Notice if your message requires many actions.'
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const snackbar = screen.getByTestId( testId );
|
|
157
|
+
const action = within( snackbar ).getByRole( 'link' );
|
|
158
|
+
|
|
159
|
+
expect( action ).toBeVisible();
|
|
160
|
+
expect( action ).toHaveTextContent( 'One' );
|
|
161
|
+
} );
|
|
162
|
+
|
|
163
|
+
it( 'should be rendered as a link when the `url` prop is set', () => {
|
|
164
|
+
render(
|
|
165
|
+
<Snackbar
|
|
166
|
+
actions={ [
|
|
167
|
+
{ label: 'View post', url: 'https://example.com' },
|
|
168
|
+
] }
|
|
169
|
+
>
|
|
170
|
+
Post updated.
|
|
171
|
+
</Snackbar>
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const snackbar = screen.getByTestId( testId );
|
|
175
|
+
const link = within( snackbar ).getByRole( 'link', {
|
|
176
|
+
name: 'View post',
|
|
177
|
+
} );
|
|
178
|
+
|
|
179
|
+
expect( link ).toHaveAttribute( 'href', 'https://example.com' );
|
|
180
|
+
} );
|
|
181
|
+
|
|
182
|
+
it( 'should be rendered as a button and call `onClick` when the `onClick` prop is set', async () => {
|
|
183
|
+
const onClick = jest.fn();
|
|
184
|
+
|
|
185
|
+
render(
|
|
186
|
+
<Snackbar actions={ [ { label: 'View post', onClick } ] }>
|
|
187
|
+
Post updated.
|
|
188
|
+
</Snackbar>
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
const snackbar = screen.getByTestId( testId );
|
|
192
|
+
const button = within( snackbar ).getByRole( 'button', {
|
|
193
|
+
name: 'View post',
|
|
194
|
+
} );
|
|
195
|
+
|
|
196
|
+
await click( button );
|
|
197
|
+
|
|
198
|
+
expect( onClick ).toHaveBeenCalledTimes( 1 );
|
|
199
|
+
} );
|
|
200
|
+
|
|
201
|
+
it( 'should be rendered as a link when the `url` prop and the `onClick` are set', () => {
|
|
202
|
+
render(
|
|
203
|
+
<Snackbar
|
|
204
|
+
actions={ [
|
|
205
|
+
{
|
|
206
|
+
label: 'View post',
|
|
207
|
+
url: 'https://example.com',
|
|
208
|
+
onClick: () => {},
|
|
209
|
+
},
|
|
210
|
+
] }
|
|
211
|
+
>
|
|
212
|
+
Post updated.
|
|
213
|
+
</Snackbar>
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const snackbar = screen.getByTestId( testId );
|
|
217
|
+
const link = within( snackbar ).getByRole( 'link', {
|
|
218
|
+
name: 'View post',
|
|
219
|
+
} );
|
|
220
|
+
expect( link ).toBeVisible();
|
|
221
|
+
} );
|
|
222
|
+
} );
|
|
223
|
+
|
|
224
|
+
describe( 'useSpokenMessage', () => {
|
|
225
|
+
it( 'should speak the given message', () => {
|
|
226
|
+
render( <Snackbar>FYI</Snackbar> );
|
|
227
|
+
|
|
228
|
+
expect( speak ).toHaveBeenCalledWith( 'FYI', 'polite' );
|
|
229
|
+
} );
|
|
230
|
+
|
|
231
|
+
it( 'should speak the given message by explicit politeness', () => {
|
|
232
|
+
render( <Snackbar politeness="assertive">Uh oh!</Snackbar> );
|
|
233
|
+
|
|
234
|
+
expect( speak ).toHaveBeenCalledWith( 'Uh oh!', 'assertive' );
|
|
235
|
+
} );
|
|
236
|
+
|
|
237
|
+
it( 'should coerce a message to a string', () => {
|
|
238
|
+
// This test assumes that `@wordpress/a11y` is capable of handling
|
|
239
|
+
// markup strings appropriately.
|
|
240
|
+
render(
|
|
241
|
+
<Snackbar>
|
|
242
|
+
With <em>emphasis</em> this time.
|
|
243
|
+
</Snackbar>
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
expect( speak ).toHaveBeenCalledWith(
|
|
247
|
+
'With <em>emphasis</em> this time.',
|
|
248
|
+
'polite'
|
|
249
|
+
);
|
|
250
|
+
} );
|
|
251
|
+
|
|
252
|
+
it( 'should not re-speak an effectively equivalent element message', () => {
|
|
253
|
+
const { rerender } = render(
|
|
254
|
+
<Snackbar>
|
|
255
|
+
With <em>emphasis</em> this time.
|
|
256
|
+
</Snackbar>
|
|
257
|
+
);
|
|
258
|
+
rerender(
|
|
259
|
+
<Snackbar>
|
|
260
|
+
With <em>emphasis</em> this time.
|
|
261
|
+
</Snackbar>
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
expect( speak ).toHaveBeenCalledTimes( 1 );
|
|
265
|
+
} );
|
|
266
|
+
} );
|
|
267
|
+
} );
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
import { click } from '@ariakit/test';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import SnackbarList from '../list';
|
|
11
|
+
|
|
12
|
+
window.scrollTo = jest.fn();
|
|
13
|
+
|
|
14
|
+
describe( 'SnackbarList', () => {
|
|
15
|
+
afterEach( () => {
|
|
16
|
+
jest.resetAllMocks();
|
|
17
|
+
} );
|
|
18
|
+
|
|
19
|
+
it( 'should get focus after a snackbar is dismissed', async () => {
|
|
20
|
+
render(
|
|
21
|
+
<SnackbarList
|
|
22
|
+
notices={ [
|
|
23
|
+
{
|
|
24
|
+
id: 'ID_1',
|
|
25
|
+
content: 'Post published.',
|
|
26
|
+
explicitDismiss: true,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'ID_2',
|
|
30
|
+
content: 'Post updated.',
|
|
31
|
+
explicitDismiss: true,
|
|
32
|
+
},
|
|
33
|
+
] }
|
|
34
|
+
onRemove={ () => {} }
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
await click(
|
|
39
|
+
screen.getAllByRole( 'button', {
|
|
40
|
+
name: 'Dismiss this notice',
|
|
41
|
+
} )[ 0 ]
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
expect( screen.getByTestId( 'snackbar-list' ) ).toHaveFocus();
|
|
45
|
+
} );
|
|
46
|
+
} );
|
package/src/snackbar/types.ts
CHANGED
|
@@ -6,7 +6,11 @@ import type { MutableRefObject, ReactNode } from 'react';
|
|
|
6
6
|
/**
|
|
7
7
|
* Internal dependencies
|
|
8
8
|
*/
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
NoticeProps,
|
|
11
|
+
NoticeChildren,
|
|
12
|
+
NoticeAction,
|
|
13
|
+
} from '../notice/types';
|
|
10
14
|
|
|
11
15
|
type SnackbarOnlyProps = {
|
|
12
16
|
/**
|
|
@@ -28,8 +32,32 @@ type SnackbarOnlyProps = {
|
|
|
28
32
|
listRef?: MutableRefObject< HTMLDivElement | null >;
|
|
29
33
|
};
|
|
30
34
|
|
|
31
|
-
export type SnackbarProps =
|
|
32
|
-
|
|
35
|
+
export type SnackbarProps = Pick<
|
|
36
|
+
NoticeProps,
|
|
37
|
+
| 'className'
|
|
38
|
+
| 'children'
|
|
39
|
+
| 'spokenMessage'
|
|
40
|
+
| 'onRemove'
|
|
41
|
+
| 'politeness'
|
|
42
|
+
| 'onDismiss'
|
|
43
|
+
> &
|
|
44
|
+
SnackbarOnlyProps & {
|
|
45
|
+
/**
|
|
46
|
+
* An array of action objects. Each member object should contain:
|
|
47
|
+
*
|
|
48
|
+
* - `label`: `string` containing the text of the button/link
|
|
49
|
+
* - `url`: `string` OR `onClick`: `( event: SyntheticEvent ) => void` to specify
|
|
50
|
+
* what the action does.
|
|
51
|
+
*
|
|
52
|
+
* The default appearance of an action button is inferred based on whether
|
|
53
|
+
* `url` or `onClick` are provided, rendering the button as a link if
|
|
54
|
+
* appropriate. If both props are provided, `url` takes precedence, and the
|
|
55
|
+
* action button will render as an anchor tag.
|
|
56
|
+
*
|
|
57
|
+
* @default []
|
|
58
|
+
*/
|
|
59
|
+
actions?: Pick< NoticeAction, 'label' | 'url' | 'onClick' >[];
|
|
60
|
+
};
|
|
33
61
|
|
|
34
62
|
export type SnackbarListProps = {
|
|
35
63
|
notices: Array<
|
package/src/tabs/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Tabs organizes content across different screens, data sets, and interactions. It
|
|
|
14
14
|
|
|
15
15
|
#### Uncontrolled Mode
|
|
16
16
|
|
|
17
|
-
Tabs can be used in an uncontrolled mode, where the component manages its own state. In this mode, the `
|
|
17
|
+
Tabs can be used in an uncontrolled mode, where the component manages its own state. In this mode, the `defaultTabId` prop can be used to set the initially selected tab. If this prop is not set, the first tab will be selected by default. In addition, in most cases where the currently active tab becomes disabled or otherwise unavailable, uncontrolled mode will automatically fall back to selecting the first available tab.
|
|
18
18
|
|
|
19
19
|
```jsx
|
|
20
20
|
import { Tabs } from '@wordpress/components';
|
|
@@ -24,25 +24,25 @@ const onSelect = ( tabName ) => {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
const MyUncontrolledTabs = () => (
|
|
27
|
-
<Tabs onSelect={onSelect}
|
|
28
|
-
<Tabs.TabList
|
|
29
|
-
<Tabs.Tab
|
|
27
|
+
<Tabs onSelect={ onSelect } defaultTabId="tab2">
|
|
28
|
+
<Tabs.TabList>
|
|
29
|
+
<Tabs.Tab tabId="tab1" title="Tab 1">
|
|
30
30
|
Tab 1
|
|
31
31
|
</Tabs.Tab>
|
|
32
|
-
<Tabs.Tab
|
|
32
|
+
<Tabs.Tab tabId="tab2" title="Tab 2">
|
|
33
33
|
Tab 2
|
|
34
34
|
</Tabs.Tab>
|
|
35
|
-
<Tabs.Tab
|
|
35
|
+
<Tabs.Tab tabId="tab3" title="Tab 3">
|
|
36
36
|
Tab 3
|
|
37
37
|
</Tabs.Tab>
|
|
38
38
|
</Tabs.TabList>
|
|
39
|
-
<Tabs.TabPanel
|
|
39
|
+
<Tabs.TabPanel tabId="tab1">
|
|
40
40
|
<p>Selected tab: Tab 1</p>
|
|
41
41
|
</Tabs.TabPanel>
|
|
42
|
-
<Tabs.TabPanel
|
|
42
|
+
<Tabs.TabPanel tabId="tab2">
|
|
43
43
|
<p>Selected tab: Tab 2</p>
|
|
44
44
|
</Tabs.TabPanel>
|
|
45
|
-
<Tabs.TabPanel
|
|
45
|
+
<Tabs.TabPanel tabId="tab3">
|
|
46
46
|
<p>Selected tab: Tab 3</p>
|
|
47
47
|
</Tabs.TabPanel>
|
|
48
48
|
</Tabs>
|
|
@@ -51,7 +51,7 @@ const MyUncontrolledTabs = () => (
|
|
|
51
51
|
|
|
52
52
|
#### Controlled Mode
|
|
53
53
|
|
|
54
|
-
Tabs can also be used in a controlled mode, where the parent component specifies the `selectedTabId` and the `onSelect` props to control tab selection. In this mode, the `
|
|
54
|
+
Tabs can also be used in a controlled mode, where the parent component specifies the `selectedTabId` and the `onSelect` props to control tab selection. In this mode, the `defaultTabId` prop will be ignored if it is provided. If the `selectedTabId` is `null`, no tab is selected. In this mode, if the currently selected tab becomes disabled or otherwise unavailable, the component will _not_ fall back to another available tab, leaving the controlling component in charge of implementing the desired logic.
|
|
55
55
|
|
|
56
56
|
```jsx
|
|
57
57
|
import { Tabs } from '@wordpress/components';
|
|
@@ -71,24 +71,24 @@ const MyControlledTabs = () => (
|
|
|
71
71
|
onSelect( selectedId );
|
|
72
72
|
} }
|
|
73
73
|
>
|
|
74
|
-
<Tabs.TabList
|
|
75
|
-
<Tabs.Tab
|
|
74
|
+
<Tabs.TabList>
|
|
75
|
+
<Tabs.Tab tabId="tab1" title="Tab 1">
|
|
76
76
|
Tab 1
|
|
77
77
|
</Tabs.Tab>
|
|
78
|
-
<Tabs.Tab
|
|
78
|
+
<Tabs.Tab tabId="tab2" title="Tab 2">
|
|
79
79
|
Tab 2
|
|
80
80
|
</Tabs.Tab>
|
|
81
|
-
<Tabs.Tab
|
|
81
|
+
<Tabs.Tab tabId="tab3" title="Tab 3">
|
|
82
82
|
Tab 3
|
|
83
83
|
</Tabs.Tab>
|
|
84
84
|
</Tabs.TabList>
|
|
85
|
-
<Tabs.TabPanel
|
|
85
|
+
<Tabs.TabPanel tabId="tab1">
|
|
86
86
|
<p>Selected tab: Tab 1</p>
|
|
87
87
|
</Tabs.TabPanel>
|
|
88
|
-
<Tabs.TabPanel
|
|
88
|
+
<Tabs.TabPanel tabId="tab2">
|
|
89
89
|
<p>Selected tab: Tab 2</p>
|
|
90
90
|
</Tabs.TabPanel>
|
|
91
|
-
<Tabs.TabPanel
|
|
91
|
+
<Tabs.TabPanel tabId="tab3">
|
|
92
92
|
<p>Selected tab: Tab 3</p>
|
|
93
93
|
</Tabs.TabPanel>
|
|
94
94
|
</Tabs>
|
|
@@ -120,7 +120,7 @@ When `true`, the tab will be selected when receiving focus (automatic tab activa
|
|
|
120
120
|
- Required: No
|
|
121
121
|
- Default: `true`
|
|
122
122
|
|
|
123
|
-
###### `
|
|
123
|
+
###### `defaultTabId`: `string`
|
|
124
124
|
|
|
125
125
|
The id of the tab to be selected upon mounting of component. If this prop is not set, the first tab will be selected by default. The id provided will be internally prefixed with a unique instance ID to avoid collisions.
|
|
126
126
|
|
package/src/tabs/index.tsx
CHANGED
|
@@ -26,7 +26,7 @@ import { TabPanel } from './tabpanel';
|
|
|
26
26
|
|
|
27
27
|
function Tabs( {
|
|
28
28
|
selectOnMove = true,
|
|
29
|
-
|
|
29
|
+
defaultTabId,
|
|
30
30
|
orientation = 'horizontal',
|
|
31
31
|
onSelect,
|
|
32
32
|
children,
|
|
@@ -36,7 +36,7 @@ function Tabs( {
|
|
|
36
36
|
const store = Ariakit.useTabStore( {
|
|
37
37
|
selectOnMove,
|
|
38
38
|
orientation,
|
|
39
|
-
defaultSelectedId:
|
|
39
|
+
defaultSelectedId: defaultTabId && `${ instanceId }-${ defaultTabId }`,
|
|
40
40
|
setSelectedId: ( selectedId ) => {
|
|
41
41
|
const strippedDownId =
|
|
42
42
|
typeof selectedId === 'string'
|
|
@@ -66,7 +66,7 @@ function Tabs( {
|
|
|
66
66
|
return ! item.dimmed;
|
|
67
67
|
} );
|
|
68
68
|
const initialTab = items.find(
|
|
69
|
-
( item ) => item.id === `${ instanceId }-${
|
|
69
|
+
( item ) => item.id === `${ instanceId }-${ defaultTabId }`
|
|
70
70
|
);
|
|
71
71
|
|
|
72
72
|
// Handle selecting the initial tab.
|
|
@@ -78,8 +78,8 @@ function Tabs( {
|
|
|
78
78
|
// Wait for the denoted initial tab to be declared before making a
|
|
79
79
|
// selection. This ensures that if a tab is declared lazily it can
|
|
80
80
|
// still receive initial selection, as well as ensuring no tab is
|
|
81
|
-
// selected if an invalid `
|
|
82
|
-
if (
|
|
81
|
+
// selected if an invalid `defaultTabId` is provided.
|
|
82
|
+
if ( defaultTabId && ! initialTab ) {
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -101,7 +101,7 @@ function Tabs( {
|
|
|
101
101
|
}, [
|
|
102
102
|
firstEnabledTab,
|
|
103
103
|
initialTab,
|
|
104
|
-
|
|
104
|
+
defaultTabId,
|
|
105
105
|
isControlled,
|
|
106
106
|
items,
|
|
107
107
|
selectedId,
|
|
@@ -122,7 +122,7 @@ function Tabs( {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// If the currently selected tab becomes disabled, fall back to the
|
|
125
|
-
// `
|
|
125
|
+
// `defaultTabId` if possible. Otherwise select the first
|
|
126
126
|
// enabled tab (if there is one).
|
|
127
127
|
if ( initialTab && ! initialTab.dimmed ) {
|
|
128
128
|
setSelectedId( initialTab.id );
|
|
@@ -73,7 +73,7 @@ const DisabledTabTemplate: StoryFn< typeof Tabs > = ( props ) => {
|
|
|
73
73
|
return (
|
|
74
74
|
<Tabs { ...props }>
|
|
75
75
|
<Tabs.TabList>
|
|
76
|
-
<Tabs.Tab tabId="tab1" disabled
|
|
76
|
+
<Tabs.Tab tabId="tab1" disabled>
|
|
77
77
|
Tab 1
|
|
78
78
|
</Tabs.Tab>
|
|
79
79
|
<Tabs.Tab tabId="tab2">Tab 2</Tabs.Tab>
|