Skip to content

Commit 534400e

Browse files
[11.8] [Bugfix] Fix replace spinner size (#11828)
(r11.8 → 11.8)
1 parent 8ecfcb6 commit 534400e

File tree

3 files changed

+230
-75
lines changed

3 files changed

+230
-75
lines changed

src/components/SearchOverlay/SearchOverlay.js

Lines changed: 107 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import Button from 'components/Button';
1515
import ToggleElementButton from 'components/ModularComponents/ToggleElementButton';
1616
import Spinner from '../Spinner';
1717
import SearchOptionsFlyout from './SearchOptionsFlyout';
18-
import { getInstanceNode } from 'helpers/getRootNode';
19-
import { isOfficeEditorMode } from 'helpers/officeEditor';
18+
import { getEndFacingChevronIcon, getStartFacingChevronIcon } from 'helpers/rightToLeft';
19+
import { isOfficeEditorMode, isSpreadsheetEditorMode } from 'helpers/officeEditor';
2020
import './SearchOverlay.scss';
2121
import '../Button/Button.scss';
2222

@@ -40,24 +40,29 @@ const propTypes = {
4040
selectPreviousResult: PropTypes.func,
4141
isProcessingSearchResults: PropTypes.bool,
4242
activeDocumentViewerKey: PropTypes.number,
43+
showReplaceSpinner: PropTypes.bool,
44+
setShowReplaceSpinner: PropTypes.func,
4345
};
4446

4547
function SearchOverlay(props) {
4648
const { t } = useTranslation();
4749
const { isSearchOverlayDisabled, searchResults, activeResultIndex, selectNextResult, selectPreviousResult, isProcessingSearchResults, activeDocumentViewerKey } = props;
4850
const { searchValue, setSearchValue, executeSearch, replaceValue, nextResultValue, setReplaceValue } = props;
49-
const { isCaseSensitive, setCaseSensitive, isWholeWord, setWholeWord, isWildcard, setWildcard, setSearchStatus, isSearchInProgress, setIsSearchInProgress } = props;
51+
const { isCaseSensitive, setCaseSensitive, isWholeWord, setWholeWord, isWildcard, setWildcard, setSearchStatus } = props;
5052
const { searchStatus, isPanelOpen } = props;
53+
const { showReplaceSpinner, setShowReplaceSpinner } = props;
5154
const [isReplaceBtnDisabled, setReplaceBtnDisabled] = useState(true);
5255
const [isReplaceAllBtnDisabled, setReplaceAllBtnDisabled] = useState(true);
53-
const [showReplaceSpinner, setShowReplaceSpinner] = useState(false);
5456
const [isReplacementRegexValid, setReplacementRegexValid] = useState(true);
5557
const [allowInitialSearch, setAllowInitialSearch] = useState(false);
5658
const [isReplaceInputActive, setisReplaceInputActive] = useState(false);
5759
const isSearchAndReplaceDisabled = useSelector((state) => selectors.isElementDisabled(state, 'searchAndReplace'));
5860
const customizableUI = useSelector((state) => selectors.getFeatureFlags(state)?.customizableUI);
5961
const searchTextInputRef = useRef();
6062
const waitTime = 300; // Wait time in milliseconds
63+
const dispatch = useDispatch();
64+
const isSearchInProgress = useSelector((state) => selectors.isSearchInProgress(state));
65+
const officeEditorIsReplaceInProgress = useSelector((state) => selectors.getOfficeEditorIsReplaceInProgress(state));
6166

6267
useEffect(() => {
6368
try {
@@ -72,6 +77,9 @@ function SearchOverlay(props) {
7277
if (numberOfResultsFound > 0) {
7378
setSearchStatus('SEARCH_DONE');
7479
}
80+
81+
setReplaceBtnDisabled(numberOfResultsFound === 0);
82+
setReplaceAllBtnDisabled(numberOfResultsFound === 0);
7583
}, [searchResults]);
7684

7785
useEffect(() => {
@@ -102,28 +110,15 @@ function SearchOverlay(props) {
102110
}
103111
}, [isCaseSensitive, isWholeWord, isWildcard, activeDocumentViewerKey]);
104112

105-
useEffect(() => {
106-
core.addEventListener('pagesUpdated', onPagesUpdated);
107-
return () => {
108-
core.removeEventListener('pagesUpdated', onPagesUpdated);
109-
};
110-
});
111-
112-
const onPagesUpdated = (e) => {
113-
if (e.linearizedUpdate) {
114-
return;
115-
}
116-
search(searchValue);
117-
};
118-
119113
const search = async (searchValue) => {
120114
if (searchValue && searchValue.length > 0) {
121-
setIsSearchInProgress(true);
115+
dispatch(actions.setSearchInProgress(true));
122116
setSearchStatus('SEARCH_IN_PROGRESS');
123117

124118
if (isOfficeEditorMode()) {
125119
await core.getDocument().getOfficeEditor().updateSearchData();
126120
}
121+
127122
executeSearch(searchValue, {
128123
caseSensitive: isCaseSensitive,
129124
wholeWord: isWholeWord,
@@ -139,13 +134,42 @@ function SearchOverlay(props) {
139134
[isCaseSensitive, isWholeWord, isWildcard]
140135
);
141136

142-
const throttleSearch = useCallback(
143-
throttle(search, waitTime),
144-
[isCaseSensitive, isWholeWord, isWildcard]
145-
);
137+
const throttleSearch = throttle(search, waitTime);
138+
const searchParamsRef = useRef({
139+
searchValue,
140+
throttleSearch,
141+
officeEditorIsReplaceInProgress
142+
});
143+
144+
useEffect(() => {
145+
searchParamsRef.current = {
146+
searchValue,
147+
throttleSearch,
148+
officeEditorIsReplaceInProgress
149+
};
150+
}, [searchValue, throttleSearch, officeEditorIsReplaceInProgress, isCaseSensitive, isWholeWord, isWildcard]);
151+
152+
useEffect(() => {
153+
core.addEventListener('pagesUpdated', onPagesUpdated);
154+
return () => {
155+
core.removeEventListener('pagesUpdated', onPagesUpdated);
156+
};
157+
});
158+
159+
const onPagesUpdated = (e) => {
160+
const { searchValue, officeEditorIsReplaceInProgress } = searchParamsRef.current;
161+
if (e.linearizedUpdate || officeEditorIsReplaceInProgress) {
162+
return;
163+
}
164+
search(searchValue);
165+
};
146166

147167
useEffect(() => {
148168
const onOfficeDocumentEdited = () => {
169+
const { searchValue, throttleSearch, officeEditorIsReplaceInProgress } = searchParamsRef.current;
170+
if (officeEditorIsReplaceInProgress) {
171+
return;
172+
}
149173
if (searchValue && searchValue.length > 0) {
150174
throttleSearch(searchValue);
151175
}
@@ -156,21 +180,21 @@ function SearchOverlay(props) {
156180
return () => {
157181
core.getDocument()?.removeEventListener('officeDocumentEdited', onOfficeDocumentEdited);
158182
};
159-
}, [searchValue]);
183+
}, []);
160184

161185
const textInputOnChange = (event) => {
162186
setSearchValue(event.target.value);
163187
debouncedSearch(event.target.value);
164188

165-
if (event.target.value && replaceValue) {
189+
if (event.target.value && numberOfResultsFound > 0) {
166190
setReplaceBtnDisabled(false);
167191
setReplaceAllBtnDisabled(false);
168192
}
169193
};
170194

171195
const replaceTextInputOnChange = (event) => {
172196
setReplaceValue(event.target.value);
173-
if (event.target.value && searchValue) {
197+
if (event.target.value && searchValue && numberOfResultsFound > 0) {
174198
setReplaceBtnDisabled(false);
175199
setReplaceAllBtnDisabled(false);
176200
}
@@ -224,46 +248,64 @@ function SearchOverlay(props) {
224248
[selectPreviousResult, searchResults, activeResultIndex],
225249
);
226250

251+
const toggleReplaceInput = () => {
252+
setisReplaceInputActive(!isReplaceInputActive);
253+
};
254+
255+
const retriggerSearch = () => {
256+
if (isOfficeEditorMode()) {
257+
search(searchValue);
258+
return;
259+
}
260+
261+
if (isSpreadsheetEditorMode()) {
262+
const previousSearchValue = searchValue;
263+
const previousReplaceValue = replaceValue;
264+
clearSearchResult();
265+
setSearchValue(previousSearchValue);
266+
setReplaceValue(previousReplaceValue);
267+
268+
const shouldEnableReplaceButtons = core.getPageSearchResults()?.length > 0;
269+
if (shouldEnableReplaceButtons) {
270+
setReplaceBtnDisabled(false);
271+
setReplaceAllBtnDisabled(false);
272+
}
273+
}
274+
};
275+
227276
const searchAndReplaceAll = useCallback(
228277
async function searchAndReplaceAllCallback() {
229278
if (isReplaceAllBtnDisabled && nextResultValue) {
230279
return;
231280
}
281+
232282
setShowReplaceSpinner(true);
233-
await getInstanceNode().instance.Core.ContentEdit.searchAndReplaceText({
234-
documentViewer: getInstanceNode().instance.Core.documentViewer,
235-
searchResults: core.getPageSearchResults(),
236-
replaceWith: replaceValue,
237-
});
283+
const results = core.getPageSearchResults();
284+
const documentViewer = core.getDocumentViewer();
285+
await documentViewer.replace(results, replaceValue);
286+
retriggerSearch();
238287
setShowReplaceSpinner(false);
288+
setReplaceAllBtnDisabled(true);
239289
},
240290
[replaceValue]
241291
);
242292

243-
const toggleReplaceInput = () => {
244-
setisReplaceInputActive(!isReplaceInputActive);
245-
};
246-
247293
const searchAndReplaceOne = useCallback(
248294
async function searchAndReplaceOneCallback() {
249295
if (isReplaceBtnDisabled && nextResultValue) {
250296
return;
251297
}
252-
setShowReplaceSpinner(true);
253-
254-
await getInstanceNode().instance.Core.ContentEdit.searchAndReplaceText({
255-
documentViewer: getInstanceNode().instance.Core.documentViewer,
256-
replaceWith: replaceValue,
257-
searchResults: [core.getActiveSearchResult()],
258-
});
259298

299+
setShowReplaceSpinner(true);
300+
const activeSearchResult = core.getActiveSearchResult();
301+
const documentViewer = core.getDocumentViewer();
302+
await documentViewer.replace([activeSearchResult], replaceValue);
303+
retriggerSearch();
260304
setShowReplaceSpinner(false);
261305
},
262306
[replaceValue, nextResultValue, isReplaceBtnDisabled]
263307
);
264308

265-
const dispatch = useDispatch();
266-
267309
const replaceAllConfirmationWarning = () => {
268310
const title = t('option.searchPanel.replaceText');
269311
const message = t('option.searchPanel.confirmMessageReplaceAll');
@@ -359,7 +401,7 @@ function SearchOverlay(props) {
359401
</div>
360402
</div>
361403
{shouldShowReplaceInput && (
362-
<div data-element="searchAndReplace" className="replace-options">
404+
<div data-element={DataElements.SEARCH_PANEL_REPLACE_CONTAINER} className="replace-options">
363405
<div className="input-container with-replace-icon">
364406
<Icon
365407
disabled={false}
@@ -373,13 +415,22 @@ function SearchOverlay(props) {
373415
/>
374416
</div>
375417
<div className='replace-buttons'>
376-
{(showReplaceSpinner) ? <Spinner width={25} height={25} /> : null}
377-
<button className='Button btn-replace-all' disabled={isReplaceAllBtnDisabled}
378-
aria-label={t('option.searchPanel.replaceAll')}
379-
onClick={replaceAllConfirmationWarning}>{t('option.searchPanel.replaceAll')}</button>
380-
<button className='Button btn-replace' disabled={isReplaceBtnDisabled || !nextResultValue || !core.getActiveSearchResult()}
381-
aria-label={t('option.searchPanel.replace')}
382-
onClick={replaceOneConfirmationWarning}>{t('option.searchPanel.replace')}</button>
418+
{(showReplaceSpinner) ? <Spinner width={'25px'} height={'25px'} /> : null}
419+
<Button
420+
onClick={replaceAllConfirmationWarning}
421+
title={t('option.searchPanel.replaceAll')}
422+
ariaLabel={t('option.searchPanel.replaceAll')}
423+
className={'btn-replace-all'}
424+
disabled={isReplaceAllBtnDisabled}
425+
>{t('option.searchPanel.replaceAll')}</Button>
426+
427+
<Button
428+
onClick={replaceOneConfirmationWarning}
429+
title={t('option.searchPanel.replace')}
430+
ariaLabel={t('option.searchPanel.replace')}
431+
className={'btn-replace'}
432+
disabled={isReplaceBtnDisabled || !nextResultValue || !core.getActiveSearchResult()}
433+
>{t('option.searchPanel.replace')}</Button>
383434
</div>
384435
</div>
385436
)}
@@ -398,11 +449,11 @@ function SearchOverlay(props) {
398449
<p className="no-margin" aria-live="assertive">{isSearchDoneAndNotProcessingResults && !isSearchInProgress ? `${numberOfResultsFound} ${t('message.numResultsFound')}` : undefined}</p>
399450
{numberOfResultsFound > 0 && (
400451
<div className="buttons">
401-
<button className="button" onClick={previousButtonOnClick} aria-label={t('action.prevResult')}>
402-
<Icon className="arrow" glyph="icon-chevron-left" />
452+
<button className="button" onClick={previousButtonOnClick} title={t('action.prevResult')} aria-label={t('action.prevResult')}>
453+
<Icon className="arrow" glyph={getStartFacingChevronIcon()} />
403454
</button>
404-
<button className="button" onClick={nextButtonOnClick} aria-label={t('action.nextResult')}>
405-
<Icon className="arrow" glyph="icon-chevron-right" />
455+
<button className="button" onClick={nextButtonOnClick} title={t('action.nextResult')} aria-label={t('action.nextResult')}>
456+
<Icon className="arrow" glyph={getEndFacingChevronIcon()} />
406457
</button>
407458
</div>
408459
)}

0 commit comments

Comments
 (0)