@hero-design/rn 8.109.2 → 8.110.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.
Files changed (50) hide show
  1. package/.turbo/turbo-build.log +5 -5
  2. package/CHANGELOG.md +12 -0
  3. package/es/index.js +2142 -1959
  4. package/lib/index.js +2142 -1959
  5. package/package.json +8 -8
  6. package/src/components/Accordion/__tests__/__snapshots__/AccordionItem.spec.tsx.snap +40 -24
  7. package/src/components/Accordion/__tests__/__snapshots__/index.spec.tsx.snap +60 -36
  8. package/src/components/Accordion/index.tsx +6 -1
  9. package/src/components/AppCue/__tests__/__snapshots__/StyledAppCue.tsx.snap +121 -97
  10. package/src/components/BottomSheet/__tests__/__snapshots__/index.spec.tsx.snap +6 -3
  11. package/src/components/Button/__tests__/__snapshots__/Button.spec.tsx.snap +18 -18
  12. package/src/components/Chip/index.tsx +6 -0
  13. package/src/components/Collapse/__tests__/__snapshots__/StyledCollapse.spec.tsx.snap +8 -5
  14. package/src/components/Collapse/__tests__/__snapshots__/index.spec.tsx.snap +20 -12
  15. package/src/components/Drawer/__tests__/__snapshots__/index.spec.tsx.snap +13 -9
  16. package/src/components/PinInput/index.tsx +6 -1
  17. package/src/components/Radio/RadioGroup.tsx +6 -1
  18. package/src/components/Rate/__tests__/__snapshots__/index.spec.tsx.snap +84 -48
  19. package/src/components/RichTextEditor/BaseRichTextEditor.tsx +17 -0
  20. package/src/components/RichTextEditor/EditorToolbar.tsx +18 -59
  21. package/src/components/RichTextEditor/__tests__/EditorToolbar.spec.tsx +31 -10
  22. package/src/components/RichTextEditor/__tests__/RichTextEditor.spec.tsx +22 -0
  23. package/src/components/RichTextEditor/__tests__/__snapshots__/RichTextEditor.spec.tsx.snap +8 -0
  24. package/src/components/RichTextEditor/constants.ts +4 -0
  25. package/src/components/RichTextEditor/hooks/useRichTextEditorEvents.ts +22 -3
  26. package/src/components/Search/SearchOneLine.tsx +6 -0
  27. package/src/components/Search/SearchTwoLine.tsx +6 -1
  28. package/src/components/Select/MultiSelect/__tests__/__snapshots__/index.spec.tsx.snap +6 -3
  29. package/src/components/Select/SingleSelect/__tests__/__snapshots__/index.spec.tsx.snap +6 -3
  30. package/src/components/Switch/__tests__/__snapshots__/StyledSwitch.spec.tsx.snap +9 -6
  31. package/src/components/Tabs/__tests__/__snapshots__/ScrollableTabs.spec.tsx.snap +210 -138
  32. package/src/components/Tabs/__tests__/__snapshots__/ScrollableTabsHeader.spec.tsx.snap +98 -62
  33. package/src/components/Toolbar/ToolbarGroup.tsx +10 -2
  34. package/src/components/Toolbar/__tests__/ToolbarGroup.spec.tsx +34 -1
  35. package/src/components/Toolbar/__tests__/__snapshots__/ToolbarGroup.spec.tsx.snap +3 -0
  36. package/stats/8.109.2/rn-stats.html +1 -3
  37. package/stats/8.110.0/rn-stats.html +4844 -0
  38. package/stats/8.110.1/rn-stats.html +4844 -0
  39. package/types/components/Accordion/index.d.ts +5 -1
  40. package/types/components/Chip/index.d.ts +5 -1
  41. package/types/components/PinInput/index.d.ts +4 -0
  42. package/types/components/Radio/RadioGroup.d.ts +5 -1
  43. package/types/components/Radio/index.d.ts +1 -1
  44. package/types/components/RichTextEditor/constants.d.ts +6 -2
  45. package/types/components/RichTextEditor/hooks/useRichTextEditorEvents.d.ts +17 -2
  46. package/types/components/RichTextEditor/index.d.ts +54 -2
  47. package/types/components/Search/SearchOneLine.d.ts +4 -0
  48. package/types/components/Search/SearchTwoLine.d.ts +4 -0
  49. package/types/components/Toolbar/ToolbarGroup.d.ts +5 -1
  50. package/types/components/Toolbar/index.d.ts +1 -1
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
2
  import type { ComponentType } from 'react';
3
3
  import Icon from '../Icon';
4
4
  import { EditorEvents, ToolbarEvents } from './constants';
@@ -96,12 +96,6 @@ export interface EditorToolbarProps {
96
96
  testID?: string;
97
97
  }
98
98
 
99
- type ButtonType = {
100
- id: number;
101
- buttonName: ToolbarButtonName;
102
- selected: boolean;
103
- };
104
-
105
99
  const EditorToolbar = ({
106
100
  name,
107
101
  buttons = defaultButtons,
@@ -109,19 +103,10 @@ const EditorToolbar = ({
109
103
  }: EditorToolbarProps) => {
110
104
  const [show, setShow] = useState(false);
111
105
 
112
- const initialToolbarButtonArray: ButtonType[] = buttons.map(
113
- (button, index) => ({
114
- id: index + 1,
115
- buttonName: button,
116
- selected: false,
117
- })
118
- );
119
-
120
- const [toolbarButtonArray, setToolbarButtonArray] = useState<ButtonType[]>(
121
- initialToolbarButtonArray
122
- );
123
-
124
106
  const { emitEvent, subscribeToEvents } = useRichTextEditorEvents(name);
107
+ const [toolbarButtonState, setToolbarButtonState] = useState<
108
+ Record<string, boolean>
109
+ >({});
125
110
 
126
111
  useEffect(() => {
127
112
  const removeFocusListener = subscribeToEvents(
@@ -131,72 +116,46 @@ const EditorToolbar = ({
131
116
  const removeBlurListener = subscribeToEvents(EditorEvents.EditorBlur, () =>
132
117
  setShow(false)
133
118
  );
119
+ const removeEditorChangeListener = subscribeToEvents(
120
+ EditorEvents.EditorChange,
121
+ ({ toolbarState = {} }) => {
122
+ setToolbarButtonState(toolbarState);
123
+ }
124
+ );
134
125
 
135
126
  return () => {
136
127
  removeFocusListener();
137
128
  removeBlurListener();
129
+ removeEditorChangeListener();
138
130
  };
139
131
  }, []);
140
132
 
141
- const toggleToolbarButton = useCallback(
142
- ({ buttonName: currentButtonName, selected: prevSelected }: ButtonType) => {
143
- const currentButtonConfig = buttonConfigs[currentButtonName];
144
- const isStandalone =
145
- currentButtonConfig && currentButtonConfig.standalone;
146
-
147
- setToolbarButtonArray((prevState) =>
148
- prevState.map((updatingButton) => {
149
- if (updatingButton.buttonName === currentButtonName) {
150
- return {
151
- ...updatingButton,
152
- selected: !prevSelected,
153
- };
154
- }
155
-
156
- const updatingButtonConfig = buttonConfigs[updatingButton.buttonName];
157
- const shouldToggleOff =
158
- !prevSelected &&
159
- isStandalone &&
160
- updatingButtonConfig &&
161
- updatingButtonConfig.standalone;
162
-
163
- return {
164
- ...updatingButton,
165
- selected: shouldToggleOff ? false : updatingButton.selected,
166
- };
167
- })
168
- );
169
- },
170
- []
171
- );
172
-
173
133
  const toolbarButtons = useMemo(
174
134
  () =>
175
- toolbarButtonArray.map((button) => {
176
- if (button.buttonName === '|') {
177
- return <StyledSeparator key={button.id} />;
135
+ buttons.map((button, index) => {
136
+ if (button === '|') {
137
+ return <StyledSeparator key={index} />;
178
138
  }
179
- const config = buttonConfigs[button.buttonName];
139
+ const config = buttonConfigs[button];
180
140
  if (config) {
181
141
  return (
182
142
  <ToolbarButton
183
- key={button.id}
143
+ key={button}
184
144
  testID={config.icon}
185
145
  icon={config.icon}
186
146
  onPress={() => {
187
- toggleToolbarButton(button);
188
147
  emitEvent({
189
148
  type: config.eventName,
190
149
  data: null,
191
150
  });
192
151
  }}
193
- selected={button.selected}
152
+ selected={toolbarButtonState[config.eventName]}
194
153
  />
195
154
  );
196
155
  }
197
156
  return null;
198
157
  }),
199
- [toolbarButtonArray]
158
+ [toolbarButtonState, buttons]
200
159
  );
201
160
 
202
161
  if (show) {
@@ -3,7 +3,7 @@ import { act, fireEvent, waitFor } from '@testing-library/react-native';
3
3
  import type { RenderAPI } from '@testing-library/react-native';
4
4
  import renderWithTheme from '../../../testHelpers/renderWithTheme';
5
5
  import EditorToolbar from '../EditorToolbar';
6
- import { emitter as editorEventEmmitor } from '../EditorEvent';
6
+ import { emitter as editorEventEmitter } from '../EditorEvent';
7
7
  import { theme } from '../../../index';
8
8
  import * as Events from '../utils/events';
9
9
 
@@ -24,7 +24,7 @@ describe('EditorToolbar', () => {
24
24
  <EditorToolbar name="toolbar" testID="toolbar" />
25
25
  );
26
26
  act(() => {
27
- editorEventEmmitor.emit('toolbar/editor-focus');
27
+ editorEventEmitter.emit('toolbar/editor-focus');
28
28
  });
29
29
  await waitFor(() => wrapper.getByTestId('toolbar'));
30
30
  });
@@ -47,7 +47,7 @@ describe('EditorToolbar', () => {
47
47
 
48
48
  it('should hide toolbar when blur', async () => {
49
49
  act(() => {
50
- editorEventEmmitor.emit('toolbar/editor-blur');
50
+ editorEventEmitter.emit('toolbar/editor-blur');
51
51
  });
52
52
  await waitFor(() => wrapper.queryAllByTestId('toolbar').length === 0);
53
53
  expect(wrapper.queryAllByTestId('toolbar')).toHaveLength(0);
@@ -55,7 +55,7 @@ describe('EditorToolbar', () => {
55
55
 
56
56
  describe("should change button's background color when pressing", () => {
57
57
  it('should send event and highlight buttons correctly', () => {
58
- const emmitedEvents: string[] = [];
58
+ const emittedEvents: string[] = [];
59
59
 
60
60
  const eventNameAndTestIDArray = [
61
61
  {
@@ -89,8 +89,8 @@ describe('EditorToolbar', () => {
89
89
  ];
90
90
 
91
91
  eventNameAndTestIDArray.forEach(({ eventName, testID }) => {
92
- Events.on(editorEventEmmitor, eventName, () => {
93
- emmitedEvents.push(testID);
92
+ Events.on(editorEventEmitter, eventName, () => {
93
+ emittedEvents.push(testID);
94
94
  });
95
95
 
96
96
  expect(wrapper.getByTestId(testID)).toHaveStyle({
@@ -98,9 +98,6 @@ describe('EditorToolbar', () => {
98
98
  });
99
99
 
100
100
  fireEvent(wrapper.getByTestId(testID), 'press');
101
- expect(wrapper.getByTestId(testID)).toHaveStyle({
102
- backgroundColor: theme.colors.highlightedSurface,
103
- });
104
101
  });
105
102
 
106
103
  const standaloneButtonTestIDs = [
@@ -114,7 +111,7 @@ describe('EditorToolbar', () => {
114
111
  });
115
112
  });
116
113
 
117
- expect(emmitedEvents).toMatchObject([
114
+ expect(emittedEvents).toMatchObject([
118
115
  'format-bold',
119
116
  'format-italic',
120
117
  'format-underlined',
@@ -124,6 +121,30 @@ describe('EditorToolbar', () => {
124
121
  'format-heading2',
125
122
  ]);
126
123
  });
124
+
125
+ it('should highlight buttons correctly', () => {
126
+ act(() => {
127
+ editorEventEmitter.emit('toolbar/editor-change', {
128
+ toolbarState: {
129
+ bold: true,
130
+ italic: true,
131
+ underline: true,
132
+ 'heading-one': true,
133
+ },
134
+ });
135
+ });
136
+
137
+ [
138
+ 'format-bold',
139
+ 'format-italic',
140
+ 'format-underlined',
141
+ 'format-heading1',
142
+ ].forEach((testID) => {
143
+ expect(wrapper.getByTestId(testID)).toHaveStyle({
144
+ backgroundColor: theme.colors.highlightedSurface,
145
+ });
146
+ });
147
+ });
127
148
  });
128
149
  });
129
150
  });
@@ -206,6 +206,28 @@ describe('RichTextEditor', () => {
206
206
  });
207
207
  });
208
208
  });
209
+
210
+ describe('received event request-upsert-link', () => {
211
+ it('should emit correct data', () => {
212
+ const emittedEvents: string[] = [];
213
+ Events.on(
214
+ EditorEventEmitter,
215
+ 'rich-text-editor/request-upsert-link',
216
+ () => {
217
+ emittedEvents.push('called');
218
+ }
219
+ );
220
+
221
+ act(() => {
222
+ onMessageOfLatestRendering({
223
+ nativeEvent: {
224
+ data: '{"type": "@hero-editor/webview/request-upsert-link", "data" : {}}',
225
+ },
226
+ });
227
+ });
228
+ expect(emittedEvents).toMatchObject(['called']);
229
+ });
230
+ });
209
231
  });
210
232
 
211
233
  describe('RichTextEditorRef methods', () => {
@@ -156,6 +156,7 @@ exports[`RichTextEditor onMessage received event editor-layout should update hei
156
156
  }
157
157
  }
158
158
  accessible={true}
159
+ bounces={false}
159
160
  focusable={true}
160
161
  hideKeyboardAccessoryView={true}
161
162
  keyboardDisplayRequiresUserAction={false}
@@ -185,6 +186,9 @@ exports[`RichTextEditor onMessage received event editor-layout should update hei
185
186
  body {
186
187
  margin: 0;
187
188
  }
189
+ a {
190
+ color: #401960;
191
+ }
188
192
  </style>
189
193
  </head>
190
194
  <body>
@@ -494,6 +498,7 @@ exports[`RichTextEditor should render correctly 1`] = `
494
498
  }
495
499
  }
496
500
  accessible={true}
501
+ bounces={false}
497
502
  focusable={true}
498
503
  hideKeyboardAccessoryView={true}
499
504
  keyboardDisplayRequiresUserAction={false}
@@ -523,6 +528,9 @@ exports[`RichTextEditor should render correctly 1`] = `
523
528
  body {
524
529
  margin: 0;
525
530
  }
531
+ a {
532
+ color: #401960;
533
+ }
526
534
  </style>
527
535
  </head>
528
536
  <body>
@@ -6,9 +6,13 @@ export enum ToolbarEvents {
6
6
  NumberedList = 'numbered-list',
7
7
  HeadingOne = 'heading-one',
8
8
  HeadingTwo = 'heading-two',
9
+ AddLink = 'add-link',
10
+ ApplyLink = 'link',
9
11
  }
10
12
 
11
13
  export enum EditorEvents {
12
14
  EditorFocus = 'editor-focus',
13
15
  EditorBlur = 'editor-blur',
16
+ EditorChange = 'editor-change',
17
+ UpsertLink = 'request-upsert-link',
14
18
  }
@@ -6,6 +6,23 @@ import type { EditorEvents, ToolbarEvents } from '../constants';
6
6
 
7
7
  type SubscribableEvent = ToolbarEvents | EditorEvents;
8
8
 
9
+ type EventData = {
10
+ [EditorEvents.UpsertLink]: {
11
+ text: string;
12
+ url: string;
13
+ };
14
+ [EditorEvents.EditorChange]: {
15
+ value: string;
16
+ toolbarState: Record<string, boolean>;
17
+ };
18
+ [ToolbarEvents.ApplyLink]: {
19
+ text: string;
20
+ url: string;
21
+ };
22
+ };
23
+
24
+ type EventDataByEventName<T> = T extends keyof EventData ? EventData[T] : void;
25
+
9
26
  /**
10
27
  * Hook to subscribe to events for a rich text editor
11
28
  * @param editorName - The name of the editor to subscribe to events for
@@ -26,12 +43,12 @@ const useRichTextEditorEvents = (editorName: string) => {
26
43
  const subscribeToEvents = useCallback(
27
44
  <TEventName extends SubscribableEvent>(
28
45
  eventName: TEventName,
29
- onEvent: (data: unknown) => void
46
+ onEvent: (data: EventDataByEventName<TEventName>) => void
30
47
  ) =>
31
48
  Events.on(
32
49
  emitter,
33
50
  normalizeEventName(eventName),
34
- (eventData: unknown) => {
51
+ (eventData: EventDataByEventName<TEventName>) => {
35
52
  onEvent(eventData);
36
53
  }
37
54
  ),
@@ -44,7 +61,9 @@ const useRichTextEditorEvents = (editorName: string) => {
44
61
  data,
45
62
  }: {
46
63
  type: TEventName;
47
- data: unknown;
64
+ data: TEventName extends keyof EventData
65
+ ? EventData[TEventName]
66
+ : unknown;
48
67
  }) => {
49
68
  Events.emit(emitter, normalizeEventName(type), data);
50
69
  },
@@ -78,6 +78,10 @@ interface SearchOneLineProps extends NativeTextInputProps {
78
78
  * Clearable input.
79
79
  */
80
80
  clearable?: boolean;
81
+ /**
82
+ * If true, indicates that the SearchOneLine is accessible to screen readers.
83
+ */
84
+ accessible?: boolean;
81
85
  }
82
86
 
83
87
  export const getState = ({
@@ -129,6 +133,7 @@ const SearchOneLine = forwardRef<SearchOneLineHandles, SearchOneLineProps>(
129
133
  testID,
130
134
  variant = 'basic',
131
135
  clearable = false,
136
+ accessible,
132
137
  ...nativeProps
133
138
  } = props;
134
139
 
@@ -191,6 +196,7 @@ const SearchOneLine = forwardRef<SearchOneLineHandles, SearchOneLineProps>(
191
196
  }
192
197
  testID={testID}
193
198
  style={style}
199
+ accessible={accessible}
194
200
  >
195
201
  <StyledInputContainer themeVariant={variant}>
196
202
  {renderPrefix({ prefix })}
@@ -39,6 +39,10 @@ interface SearchTwoLineProps {
39
39
  * @default 'basic'
40
40
  */
41
41
  variant?: 'basic' | 'reversed';
42
+ /**
43
+ * If true, indicates that the SearchTwoLine is accessible to screen readers.
44
+ */
45
+ accessible?: boolean;
42
46
  }
43
47
 
44
48
  const SearchTwoLine = (props: SearchTwoLineProps) => {
@@ -50,9 +54,10 @@ const SearchTwoLine = (props: SearchTwoLineProps) => {
50
54
  content,
51
55
  testID,
52
56
  variant = 'basic',
57
+ accessible,
53
58
  } = props;
54
59
  return (
55
- <StyledContainer style={style} testID={testID}>
60
+ <StyledContainer style={style} testID={testID} accessible={accessible}>
56
61
  <StyledInputContainer themeVariant={variant}>
57
62
  {renderPrefix({ prefix })}
58
63
  <StyledHandlerContainer>
@@ -1890,9 +1890,12 @@ exports[`rendering renders correct floating bottom sheet variant 1`] = `
1890
1890
  <RCTSafeAreaView
1891
1891
  collapsable={false}
1892
1892
  style={
1893
- {
1894
- "margin": 16,
1895
- }
1893
+ [
1894
+ {
1895
+ "margin": 16,
1896
+ },
1897
+ undefined,
1898
+ ]
1896
1899
  }
1897
1900
  >
1898
1901
  <View
@@ -1802,9 +1802,12 @@ exports[`rendering renders correct floating bottom sheet variant 1`] = `
1802
1802
  <RCTSafeAreaView
1803
1803
  collapsable={false}
1804
1804
  style={
1805
- {
1806
- "margin": 16,
1807
- }
1805
+ [
1806
+ {
1807
+ "margin": 16,
1808
+ },
1809
+ undefined,
1810
+ ]
1808
1811
  }
1809
1812
  >
1810
1813
  <View
@@ -11,12 +11,15 @@ exports[`StyledKnot renders correct style 1`] = `
11
11
  <View
12
12
  collapsable={false}
13
13
  style={
14
- {
15
- "backgroundColor": "#ffffff",
16
- "borderRadius": 999,
17
- "height": 16,
18
- "width": 16,
19
- }
14
+ [
15
+ {
16
+ "backgroundColor": "#ffffff",
17
+ "borderRadius": 999,
18
+ "height": 16,
19
+ "width": 16,
20
+ },
21
+ undefined,
22
+ ]
20
23
  }
21
24
  />
22
25
  <View