@wordpress/nux 9.48.0 → 10.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,77 +1,20 @@
1
- /**
2
- * External dependencies
3
- */
4
- import { render, screen, waitFor } from '@testing-library/react';
5
- import userEvent from '@testing-library/user-event';
6
-
7
- /**
8
- * Internal dependencies
9
- */
10
- import { DotTip } from '..';
1
+ import { render } from '@testing-library/react';
2
+ import DotTip, { DotTip as NamedDotTip } from '..';
11
3
 
12
4
  describe( 'DotTip', () => {
13
- it( 'should not render anything if invisible', () => {
14
- render(
15
- <DotTip>
16
- It looks like you’re writing a letter. Would you like help?
17
- </DotTip>
18
- );
19
-
20
- expect( screen.queryByRole( 'dialog' ) ).not.toBeInTheDocument();
21
- } );
22
-
23
- it( 'should render correctly', async () => {
24
- render(
25
- <DotTip isVisible>
26
- It looks like you’re writing a letter. Would you like help?
27
- </DotTip>
28
- );
29
-
30
- // Wait for the dialog element to be positioned (aligned with the button)
31
- await waitFor( () =>
32
- expect( screen.getByRole( 'dialog' ) ).toBePositionedPopover()
33
- );
34
-
35
- expect( screen.getByRole( 'dialog' ) ).toMatchSnapshot();
36
- } );
37
-
38
- it( 'should call onDismiss when the dismiss button is clicked', async () => {
39
- const user = userEvent.setup();
40
- const onDismiss = jest.fn();
41
-
42
- render(
43
- <DotTip isVisible onDismiss={ onDismiss }>
44
- It looks like you’re writing a letter. Would you like help?
45
- </DotTip>
5
+ it( 'renders nothing when accessed through the default export', () => {
6
+ const { container } = render(
7
+ <DotTip tipId="test/tip">Tip content</DotTip>
46
8
  );
47
9
 
48
- await waitFor( () =>
49
- expect( screen.getByRole( 'dialog' ) ).toBePositionedPopover()
50
- );
51
-
52
- await user.click( screen.getByRole( 'button', { name: 'Got it' } ) );
53
-
54
- expect( onDismiss ).toHaveBeenCalled();
10
+ expect( container ).toBeEmptyDOMElement();
55
11
  } );
56
12
 
57
- it( 'should call onDisable when the X button is clicked', async () => {
58
- const user = userEvent.setup();
59
- const onDisable = jest.fn();
60
-
61
- render(
62
- <DotTip isVisible onDisable={ onDisable }>
63
- It looks like you’re writing a letter. Would you like help?
64
- </DotTip>
65
- );
66
-
67
- await waitFor( () =>
68
- expect( screen.getByRole( 'dialog' ) ).toBePositionedPopover()
69
- );
70
-
71
- await user.click(
72
- screen.getByRole( 'button', { name: 'Disable tips' } )
13
+ it( 'renders nothing when accessed through the named export', () => {
14
+ const { container } = render(
15
+ <NamedDotTip tipId="test/tip">Tip content</NamedDotTip>
73
16
  );
74
17
 
75
- expect( onDisable ).toHaveBeenCalled();
18
+ expect( container ).toBeEmptyDOMElement();
76
19
  } );
77
20
  } );
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Returns an action object that, when dispatched, presents a guide that takes
3
- * the user through a series of tips step by step.
2
+ * Returns a no-op action object. This package is deprecated and no longer
3
+ * displays tips, but the action remains for backward compatibility.
4
4
  *
5
- * @param {string[]} tipIds Which tips to show in the guide.
5
+ * @param {string[]} tipIds Which tips would have been shown in the guide.
6
6
  *
7
7
  * @return {Object} Action object.
8
8
  */
@@ -14,10 +14,10 @@ export function triggerGuide( tipIds ) {
14
14
  }
15
15
 
16
16
  /**
17
- * Returns an action object that, when dispatched, dismisses the given tip. A
18
- * dismissed tip will not show again.
17
+ * Returns a no-op action object. This package is deprecated and no longer
18
+ * displays tips, but the action remains for backward compatibility.
19
19
  *
20
- * @param {string} id The tip to dismiss.
20
+ * @param {string} id The tip that would have been dismissed.
21
21
  *
22
22
  * @return {Object} Action object.
23
23
  */
@@ -29,8 +29,8 @@ export function dismissTip( id ) {
29
29
  }
30
30
 
31
31
  /**
32
- * Returns an action object that, when dispatched, prevents all tips from
33
- * showing again.
32
+ * Returns a no-op action object. This package is deprecated and no longer
33
+ * displays tips, but the action remains for backward compatibility.
34
34
  *
35
35
  * @return {Object} Action object.
36
36
  */
@@ -41,7 +41,8 @@ export function disableTips() {
41
41
  }
42
42
 
43
43
  /**
44
- * Returns an action object that, when dispatched, makes all tips show again.
44
+ * Returns a no-op action object. This package is deprecated and no longer
45
+ * displays tips, but the action remains for backward compatibility.
45
46
  *
46
47
  * @return {Object} Action object.
47
48
  */
@@ -1,70 +1,22 @@
1
1
  /**
2
- * WordPress dependencies
2
+ * Deprecated NUX store state kept only so the core/nux store can still be
3
+ * registered for backward compatibility.
3
4
  */
4
- import { combineReducers } from '@wordpress/data';
5
+ const DEFAULT_STATE = {
6
+ guides: [],
7
+ preferences: {
8
+ areTipsEnabled: false,
9
+ dismissedTips: {},
10
+ },
11
+ };
5
12
 
6
13
  /**
7
- * Reducer that tracks which tips are in a guide. Each guide is represented by
8
- * an array which contains the tip identifiers contained within that guide.
14
+ * Reducer that preserves state without responding to actions.
9
15
  *
10
- * @param {Array} state Current state.
11
- * @param {Object} action Dispatched action.
16
+ * @param {Object} state Current state.
12
17
  *
13
- * @return {Array} Updated state.
18
+ * @return {Object} Current state.
14
19
  */
15
- export function guides( state = [], action ) {
16
- switch ( action.type ) {
17
- case 'TRIGGER_GUIDE':
18
- return [ ...state, action.tipIds ];
19
- }
20
-
20
+ export default function reducer( state = DEFAULT_STATE ) {
21
21
  return state;
22
22
  }
23
-
24
- /**
25
- * Reducer that tracks whether or not tips are globally enabled.
26
- *
27
- * @param {boolean} state Current state.
28
- * @param {Object} action Dispatched action.
29
- *
30
- * @return {boolean} Updated state.
31
- */
32
- export function areTipsEnabled( state = true, action ) {
33
- switch ( action.type ) {
34
- case 'DISABLE_TIPS':
35
- return false;
36
-
37
- case 'ENABLE_TIPS':
38
- return true;
39
- }
40
-
41
- return state;
42
- }
43
-
44
- /**
45
- * Reducer that tracks which tips have been dismissed. If the state object
46
- * contains a tip identifier, then that tip is dismissed.
47
- *
48
- * @param {Object} state Current state.
49
- * @param {Object} action Dispatched action.
50
- *
51
- * @return {Object} Updated state.
52
- */
53
- export function dismissedTips( state = {}, action ) {
54
- switch ( action.type ) {
55
- case 'DISMISS_TIP':
56
- return {
57
- ...state,
58
- [ action.id ]: true,
59
- };
60
-
61
- case 'ENABLE_TIPS':
62
- return {};
63
- }
64
-
65
- return state;
66
- }
67
-
68
- const preferences = combineReducers( { areTipsEnabled, dismissedTips } );
69
-
70
- export default combineReducers( { guides, preferences } );
@@ -1,8 +1,3 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { createSelector } from '@wordpress/data';
5
-
6
1
  /**
7
2
  * An object containing information about a guide.
8
3
  *
@@ -13,69 +8,28 @@ import { createSelector } from '@wordpress/data';
13
8
  */
14
9
 
15
10
  /**
16
- * Returns an object describing the guide, if any, that the given tip is a part
17
- * of.
11
+ * Returns null because the deprecated NUX package no longer displays guides.
18
12
  *
19
- * @param {Object} state Global application state.
20
- * @param {string} tipId The tip to query.
21
- *
22
- * @return {?NUXGuideInfo} Information about the associated guide.
13
+ * @return {null} No associated guide.
23
14
  */
24
- export const getAssociatedGuide = createSelector(
25
- ( state, tipId ) => {
26
- for ( const tipIds of state.guides ) {
27
- if ( tipIds.includes( tipId ) ) {
28
- const nonDismissedTips = tipIds.filter(
29
- ( tId ) =>
30
- ! Object.keys(
31
- state.preferences.dismissedTips
32
- ).includes( tId )
33
- );
34
- const [ currentTipId = null, nextTipId = null ] =
35
- nonDismissedTips;
36
- return { tipIds, currentTipId, nextTipId };
37
- }
38
- }
39
-
40
- return null;
41
- },
42
- ( state ) => [ state.guides, state.preferences.dismissedTips ]
43
- );
15
+ export function getAssociatedGuide() {
16
+ return null;
17
+ }
44
18
 
45
19
  /**
46
- * Determines whether or not the given tip is showing. Tips are hidden if they
47
- * are disabled, have been dismissed, or are not the current tip in any
48
- * guide that they have been added to.
49
- *
50
- * @param {Object} state Global application state.
51
- * @param {string} tipId The tip to query.
20
+ * Returns false because the deprecated NUX package no longer displays tips.
52
21
  *
53
22
  * @return {boolean} Whether or not the given tip is showing.
54
23
  */
55
- export function isTipVisible( state, tipId ) {
56
- if ( ! state.preferences.areTipsEnabled ) {
57
- return false;
58
- }
59
-
60
- if ( state.preferences.dismissedTips?.hasOwnProperty( tipId ) ) {
61
- return false;
62
- }
63
-
64
- const associatedGuide = getAssociatedGuide( state, tipId );
65
- if ( associatedGuide && associatedGuide.currentTipId !== tipId ) {
66
- return false;
67
- }
68
-
69
- return true;
24
+ export function isTipVisible() {
25
+ return false;
70
26
  }
71
27
 
72
28
  /**
73
- * Returns whether or not tips are globally enabled.
74
- *
75
- * @param {Object} state Global application state.
29
+ * Returns false because the deprecated NUX package no longer displays tips.
76
30
  *
77
31
  * @return {boolean} Whether tips are globally enabled.
78
32
  */
79
- export function areTipsEnabled( state ) {
80
- return state.preferences.areTipsEnabled;
33
+ export function areTipsEnabled() {
34
+ return false;
81
35
  }
@@ -1,69 +1,29 @@
1
- /**
2
- * Internal dependencies
3
- */
4
- import { guides, areTipsEnabled, dismissedTips } from '../reducer';
1
+ import reducer from '../reducer';
5
2
 
6
3
  describe( 'reducer', () => {
7
- describe( 'guides', () => {
8
- it( 'should start out empty', () => {
9
- expect( guides( undefined, {} ) ).toEqual( [] );
10
- } );
11
-
12
- it( 'should add a guide when it is triggered', () => {
13
- const state = guides( [], {
14
- type: 'TRIGGER_GUIDE',
15
- tipIds: [ 'test/tip-1', 'test/tip-2' ],
16
- } );
17
- expect( state ).toEqual( [ [ 'test/tip-1', 'test/tip-2' ] ] );
18
- } );
19
- } );
20
-
21
- describe( 'areTipsEnabled', () => {
22
- it( 'should default to true', () => {
23
- expect( areTipsEnabled( undefined, {} ) ).toBe( true );
24
- } );
25
-
26
- it( 'should flip when tips are disabled', () => {
27
- const state = areTipsEnabled( true, {
28
- type: 'DISABLE_TIPS',
29
- } );
30
- expect( state ).toBe( false );
31
- } );
32
-
33
- it( 'should flip when tips are enabled', () => {
34
- const state = areTipsEnabled( false, {
35
- type: 'ENABLE_TIPS',
36
- } );
37
- expect( state ).toBe( true );
4
+ it( 'defaults to disabled empty state', () => {
5
+ expect( reducer( undefined, {} ) ).toEqual( {
6
+ guides: [],
7
+ preferences: {
8
+ areTipsEnabled: false,
9
+ dismissedTips: {},
10
+ },
38
11
  } );
39
12
  } );
40
13
 
41
- describe( 'dismissedTips', () => {
42
- it( 'should start out empty', () => {
43
- expect( dismissedTips( undefined, {} ) ).toEqual( {} );
44
- } );
45
-
46
- it( 'should mark tips as dismissed', () => {
47
- const state = dismissedTips(
48
- {},
49
- {
50
- type: 'DISMISS_TIP',
51
- id: 'test/tip',
52
- }
53
- );
54
- expect( state ).toEqual( {
55
- 'test/tip': true,
56
- } );
57
- } );
14
+ it( 'ignores legacy actions', () => {
15
+ const state = reducer( undefined, {} );
58
16
 
59
- it( 'should reset if tips are enabled', () => {
60
- const initialState = {
61
- 'test/tip': true,
62
- };
63
- const state = dismissedTips( initialState, {
17
+ expect(
18
+ reducer( state, {
64
19
  type: 'ENABLE_TIPS',
65
- } );
66
- expect( state ).toEqual( {} );
67
- } );
20
+ } )
21
+ ).toBe( state );
22
+ expect(
23
+ reducer( state, {
24
+ type: 'TRIGGER_GUIDE',
25
+ tipIds: [ 'test/tip' ],
26
+ } )
27
+ ).toBe( state );
68
28
  } );
69
29
  } );
@@ -1,146 +1,21 @@
1
- /**
2
- * Internal dependencies
3
- */
4
1
  import { getAssociatedGuide, isTipVisible, areTipsEnabled } from '../selectors';
5
2
 
6
3
  describe( 'selectors', () => {
7
4
  describe( 'getAssociatedGuide', () => {
8
- const state = {
9
- guides: [
10
- [ 'test/tip-1', 'test/tip-2', 'test/tip-3' ],
11
- [ 'test/tip-a', 'test/tip-b', 'test/tip-c' ],
12
- [ 'test/tip-α', 'test/tip-β', 'test/tip-γ' ],
13
- ],
14
- preferences: {
15
- dismissedTips: {
16
- 'test/tip-1': true,
17
- 'test/tip-a': true,
18
- 'test/tip-b': true,
19
- 'test/tip-α': true,
20
- 'test/tip-β': true,
21
- 'test/tip-γ': true,
22
- },
23
- },
24
- };
25
-
26
- it( 'should return null when there is no associated guide', () => {
27
- expect( getAssociatedGuide( state, 'test/unknown' ) ).toBeNull();
28
- } );
29
-
30
- it( 'should return the associated guide', () => {
31
- expect( getAssociatedGuide( state, 'test/tip-2' ) ).toEqual( {
32
- tipIds: [ 'test/tip-1', 'test/tip-2', 'test/tip-3' ],
33
- currentTipId: 'test/tip-2',
34
- nextTipId: 'test/tip-3',
35
- } );
36
- } );
37
-
38
- it( 'should indicate when there is no next tip', () => {
39
- expect( getAssociatedGuide( state, 'test/tip-b' ) ).toEqual( {
40
- tipIds: [ 'test/tip-a', 'test/tip-b', 'test/tip-c' ],
41
- currentTipId: 'test/tip-c',
42
- nextTipId: null,
43
- } );
44
- } );
45
-
46
- it( 'should indicate when there is no current or next tip', () => {
47
- expect( getAssociatedGuide( state, 'test/tip-β' ) ).toEqual( {
48
- tipIds: [ 'test/tip-α', 'test/tip-β', 'test/tip-γ' ],
49
- currentTipId: null,
50
- nextTipId: null,
51
- } );
5
+ it( 'returns null', () => {
6
+ expect( getAssociatedGuide( {}, 'test/tip' ) ).toBeNull();
52
7
  } );
53
8
  } );
54
9
 
55
10
  describe( 'isTipVisible', () => {
56
- it( 'is tolerant to individual preferences being undefined', () => {
57
- // See: https://github.com/WordPress/gutenberg/issues/14580
58
- const state = {
59
- guides: [],
60
- preferences: {},
61
- };
62
- expect( isTipVisible( state, 'test/tip' ) ).toBe( false );
63
- } );
64
-
65
- it( 'is tolerant to undefined dismissedTips', () => {
66
- // See: https://github.com/WordPress/gutenberg/issues/14580
67
- const state = {
68
- guides: [],
69
- preferences: {
70
- areTipsEnabled: true,
71
- },
72
- };
73
- expect( isTipVisible( state, 'test/tip' ) ).toBe( true );
74
- } );
75
-
76
- it( 'should return true by default', () => {
77
- const state = {
78
- guides: [],
79
- preferences: {
80
- areTipsEnabled: true,
81
- dismissedTips: {},
82
- },
83
- };
84
- expect( isTipVisible( state, 'test/tip' ) ).toBe( true );
85
- } );
86
-
87
- it( 'should return false if tips are disabled', () => {
88
- const state = {
89
- guides: [],
90
- preferences: {
91
- areTipsEnabled: false,
92
- dismissedTips: {},
93
- },
94
- };
95
- expect( isTipVisible( state, 'test/tip' ) ).toBe( false );
96
- } );
97
-
98
- it( 'should return false if the tip is dismissed', () => {
99
- const state = {
100
- guides: [],
101
- preferences: {
102
- areTipsEnabled: true,
103
- dismissedTips: {
104
- 'test/tip': true,
105
- },
106
- },
107
- };
108
- expect( isTipVisible( state, 'test/tip' ) ).toBe( false );
109
- } );
110
-
111
- it( 'should return false if the tip is in a guide and it is not the current tip', () => {
112
- const state = {
113
- guides: [ [ 'test/tip-1', 'test/tip-2', 'test/tip-3' ] ],
114
- preferences: {
115
- areTipsEnabled: true,
116
- dismissedTips: {},
117
- },
118
- };
119
- expect( isTipVisible( state, 'test/tip-2' ) ).toBe( false );
11
+ it( 'returns false', () => {
12
+ expect( isTipVisible( {}, 'test/tip' ) ).toBe( false );
120
13
  } );
121
14
  } );
122
15
 
123
16
  describe( 'areTipsEnabled', () => {
124
- it( 'should return true if tips are enabled', () => {
125
- const state = {
126
- guides: [],
127
- preferences: {
128
- areTipsEnabled: true,
129
- dismissedTips: {},
130
- },
131
- };
132
- expect( areTipsEnabled( state ) ).toBe( true );
133
- } );
134
-
135
- it( 'should return false if tips are disabled', () => {
136
- const state = {
137
- guides: [],
138
- preferences: {
139
- areTipsEnabled: false,
140
- dismissedTips: {},
141
- },
142
- };
143
- expect( areTipsEnabled( state ) ).toBe( false );
17
+ it( 'returns false', () => {
18
+ expect( areTipsEnabled( {} ) ).toBe( false );
144
19
  } );
145
20
  } );
146
21
  } );
package/src/style.scss CHANGED
@@ -1 +1,4 @@
1
- @use "./components/dot-tip/style.scss" as *;
1
+ /**
2
+ * Intentionally no styles.
3
+ * This entry point is kept so existing @wordpress/nux style imports continue to resolve.
4
+ */
@@ -1,131 +0,0 @@
1
- @use "sass:math";
2
- @use "@wordpress/base-styles/breakpoints" as *;
3
- @use "@wordpress/base-styles/mixins" as *;
4
- @use "@wordpress/base-styles/variables" as *;
5
- @use "@wordpress/base-styles/z-index" as *;
6
-
7
- $dot-size: 8px; // Size of the indicator dot
8
- $dot-scale: 3; // How much the pulse animation should scale up by in size
9
-
10
- .nux-dot-tip {
11
- &::before,
12
- &::after {
13
- border-radius: 100%;
14
- content: " ";
15
- pointer-events: none;
16
- position: absolute;
17
- }
18
-
19
- &::before {
20
- background: rgba(#00739c, 0.9);
21
- opacity: 0.9;
22
- height: $dot-size * $dot-scale;
23
- left: -($dot-size * $dot-scale) * 0.5;
24
- top: -($dot-size * $dot-scale) * 0.5;
25
- transform: scale(math.div(1, $dot-scale));
26
- width: $dot-size * $dot-scale;
27
-
28
- @media not (prefers-reduced-motion) {
29
- animation: nux-pulse 1.6s infinite cubic-bezier(0.17, 0.67, 0.92, 0.62);
30
- }
31
- }
32
-
33
- &::after {
34
- background: #00739c;
35
- height: $dot-size;
36
- left: -$dot-size * 0.5;
37
- top: -$dot-size * 0.5;
38
- width: $dot-size;
39
- }
40
-
41
- @keyframes nux-pulse {
42
- 100% {
43
- background: rgba(#00739c, 0);
44
- transform: scale(1);
45
- }
46
- }
47
-
48
- .components-popover__content {
49
- width: 350px;
50
- padding: 20px 18px;
51
-
52
- @include break-small {
53
- width: 450px;
54
- }
55
-
56
- .nux-dot-tip__disable {
57
- position: absolute;
58
- right: 0;
59
- top: 0;
60
- }
61
- }
62
-
63
- // Position the dot right next to the edge of the button
64
- &[data-y-axis="top"] {
65
- margin-top: -$dot-size * 0.5;
66
- }
67
- &[data-y-axis="bottom"] {
68
- margin-top: $dot-size * 0.5;
69
- }
70
- &[data-y-axis="middle"][data-y-axis="left"] {
71
- margin-left: -$dot-size * 0.5;
72
- }
73
- &[data-y-axis="middle"][data-y-axis="right"] {
74
- margin-left: $dot-size * 0.5;
75
- }
76
-
77
- // Position the tip content away from the dot
78
- &[data-y-axis="top"] .components-popover__content {
79
- margin-bottom: 20px;
80
- }
81
- &[data-y-axis="bottom"] .components-popover__content {
82
- margin-top: 20px;
83
- }
84
- &[data-y-axis="middle"][data-y-axis="left"] .components-popover__content {
85
- margin-right: 20px;
86
- }
87
- &[data-y-axis="middle"][data-y-axis="right"] .components-popover__content {
88
- margin-left: 20px;
89
- }
90
-
91
- // Extra specificity so that we can override the styles in .component-popover
92
- &[data-y-axis="left"],
93
- &[data-y-axis="center"],
94
- &[data-y-axis="right"] {
95
- // Position tips above popovers
96
- z-index: z-index(".nux-dot-tip");
97
-
98
- // On mobile, always position the tip below the dot and fill the width of the viewport
99
- @media (max-width: $break-small) {
100
- .components-popover__content {
101
- align-self: end;
102
- left: 5px;
103
- margin: 20px 0 0 0;
104
- max-width: none !important; // Override the inline style set by <Popover>
105
- position: fixed;
106
- right: 5px;
107
- width: auto;
108
- }
109
- }
110
- }
111
-
112
- &.components-popover:not([data-y-axis="middle"])[data-y-axis="right"] .components-popover__content {
113
- /*!rtl:ignore*/
114
- margin-left: 0;
115
- }
116
-
117
- &.components-popover:not([data-y-axis="middle"])[data-y-axis="left"] .components-popover__content {
118
- /*!rtl:ignore*/
119
- margin-right: 0;
120
- }
121
-
122
- &.components-popover.interface-more-menu-dropdown__content:not([data-y-axis="middle"])[data-y-axis="right"] .components-popover__content {
123
- /*!rtl:ignore*/
124
- margin-left: -12px;
125
- }
126
-
127
- &.components-popover.interface-more-menu-dropdown__content:not([data-y-axis="middle"])[data-y-axis="left"] .components-popover__content {
128
- /*!rtl:ignore*/
129
- margin-right: -12px;
130
- }
131
- }