diff --git a/.changeset/wicked-clubs-dream.md b/.changeset/wicked-clubs-dream.md new file mode 100644 index 0000000000..c866ac6312 --- /dev/null +++ b/.changeset/wicked-clubs-dream.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-search-react': patch +'@backstage/plugin-search': patch +--- + +search plugin support i18n diff --git a/plugins/search-react/report-alpha.api.md b/plugins/search-react/report-alpha.api.md index 98a3c533db..d3763bfcbf 100644 --- a/plugins/search-react/report-alpha.api.md +++ b/plugins/search-react/report-alpha.api.md @@ -8,6 +8,7 @@ import { ExtensionBlueprint } from '@backstage/frontend-plugin-api'; import { ListItemProps } from '@material-ui/core/ListItem'; import { SearchDocument } from '@backstage/plugin-search-common'; import { SearchResult } from '@backstage/plugin-search-common'; +import { TranslationRef } from '@backstage/core-plugin-api/alpha'; // @alpha (undocumented) export type BaseSearchResultListItemProps = T & { @@ -94,6 +95,23 @@ export interface SearchFilterResultTypeBlueprintParams { value: string; } +// @alpha (undocumented) +export const searchReactTranslationRef: TranslationRef< + 'search-react', + { + readonly 'searchBar.title': 'Search'; + readonly 'searchBar.placeholder': 'Search in {{org}}'; + readonly 'searchFilter.allOptionTitle': 'All'; + readonly 'searchPagination.limitLabel': 'Results per page:'; + readonly 'searchPagination.limitText': 'of {{num}}'; + readonly noResultsDescription: 'Sorry, no results were found'; + readonly 'searchResultGroup.linkTitle': 'See All'; + readonly 'searchResultGroup.addFilterButtonTitle': 'Add filter'; + readonly 'searchResultPager.next': 'Next'; + readonly 'searchResultPager.previous': 'Previous'; + } +>; + // @alpha (undocumented) export type SearchResultItemExtensionComponent = < P extends BaseSearchResultListItemProps, diff --git a/plugins/search-react/src/alpha/index.ts b/plugins/search-react/src/alpha/index.ts index 239ffa89dd..7c44f59d56 100644 --- a/plugins/search-react/src/alpha/index.ts +++ b/plugins/search-react/src/alpha/index.ts @@ -14,3 +14,4 @@ * limitations under the License. */ export * from './blueprints'; +export { searchReactTranslationRef } from '../translation'; diff --git a/plugins/search-react/src/components/SearchBar/SearchBar.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.tsx index 98bfe81b32..d2a1d08283 100644 --- a/plugins/search-react/src/components/SearchBar/SearchBar.tsx +++ b/plugins/search-react/src/components/SearchBar/SearchBar.tsx @@ -38,6 +38,8 @@ import { } from 'react'; import useDebounce from 'react-use/esm/useDebounce'; import { SearchContextProvider, useSearch } from '../../context'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { searchReactTranslationRef } from '../../translation'; /** * Props for {@link SearchBarBase}. @@ -81,6 +83,7 @@ export const SearchBarBase = forwardRef((props: SearchBarBaseProps, ref) => { const configApi = useApi(configApiRef); const [value, setValue] = useState(''); const forwardedValueRef = useRef(''); + const { t } = useTranslationRef(searchReactTranslationRef); useEffect(() => { setValue(prevValue => { @@ -129,12 +132,15 @@ export const SearchBarBase = forwardRef((props: SearchBarBaseProps, ref) => { } }, [onChange, onClear]); - const ariaLabel: string | undefined = label ? undefined : 'Search'; + const ariaLabel: string | undefined = label + ? undefined + : t('searchBar.title'); const inputPlaceholder = placeholder ?? - `Search in ${configApi.getOptionalString('app.title') || 'Backstage'}`; - + t('searchBar.placeholder', { + org: configApi.getOptionalString('app.title') || 'Backstage', + }); const SearchIcon = useApp().getSystemIcon('search') || DefaultSearchIcon; const startAdornment = ( diff --git a/plugins/search-react/src/components/SearchFilter/SearchFilter.test.tsx b/plugins/search-react/src/components/SearchFilter/SearchFilter.test.tsx index 736e3f3f93..d8d3b75aa2 100644 --- a/plugins/search-react/src/components/SearchFilter/SearchFilter.test.tsx +++ b/plugins/search-react/src/components/SearchFilter/SearchFilter.test.tsx @@ -14,14 +14,18 @@ * limitations under the License. */ -import { screen, render, waitFor } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { configApiRef } from '@backstage/core-plugin-api'; import { SearchContextProvider } from '../../context'; import { SearchFilter } from './SearchFilter'; -import { mockApis, TestApiProvider } from '@backstage/test-utils'; +import { + mockApis, + renderInTestApp, + TestApiProvider, +} from '@backstage/test-utils'; import { searchApiRef } from '../../api'; describe('SearchFilter', () => { @@ -55,14 +59,16 @@ describe('SearchFilter', () => { it('Check that element was rendered and received props', async () => { const CustomFilter = (props: { name: string }) =>
{props.name}
; - render(); + await renderInTestApp( + , + ); expect(screen.getByRole('heading', { name })).toBeInTheDocument(); }); describe('Checkbox', () => { it('Renders field name and values when provided as props', async () => { - render( + await renderInTestApp( { }); it('Renders correctly based on filter state', async () => { - render( + await renderInTestApp( { }); it('Renders correctly based on defaultValue', async () => { - render( + await renderInTestApp( { }); it('Checking / unchecking a value sets filter state', async () => { - render( + await renderInTestApp( { }); it('Checking / unchecking a value maintains unrelated filter state', async () => { - render( + await renderInTestApp( { describe('Select', () => { it('Renders field name and values when provided as props', async () => { - render( + await renderInTestApp( { }); it('Renders values when provided asynchronously', async () => { - render( + await renderInTestApp( { }); it('Renders correctly based on filter state', async () => { - render( + await renderInTestApp( { }); it('Renders correctly based on defaultValue', async () => { - render( + await renderInTestApp( { }); it('Selecting a value sets filter state', async () => { - render( + await renderInTestApp( { }); it('Selecting a value maintains unrelated filter state', async () => { - render( + await renderInTestApp( { values: givenValues, valuesDebounceMs, } = props; + const { t } = useTranslationRef(searchReactTranslationRef); useDefaultFilterValue(name, defaultValue); const asyncValues = typeof givenValues === 'function' ? givenValues : undefined; @@ -179,7 +182,10 @@ export const SelectFilter = (props: SearchFilterComponentProps) => { valuesDebounceMs, ); const allOptionValue = useRef(uuid()); - const allOption = { value: allOptionValue.current, label: 'All' }; + const allOption = { + value: allOptionValue.current, + label: t('searchFilter.allOptionTitle'), + }; const { filters, setFilters } = useSearch(); const handleChange = (value: SelectedItems) => { diff --git a/plugins/search-react/src/components/SearchPagination/SearchPagination.test.tsx b/plugins/search-react/src/components/SearchPagination/SearchPagination.test.tsx index 94d90cff59..d601caadd9 100644 --- a/plugins/search-react/src/components/SearchPagination/SearchPagination.test.tsx +++ b/plugins/search-react/src/components/SearchPagination/SearchPagination.test.tsx @@ -19,7 +19,7 @@ import userEvent from '@testing-library/user-event'; import { mockApis, - renderWithEffects, + renderInTestApp, TestApiProvider, } from '@backstage/test-utils'; @@ -53,7 +53,7 @@ describe('SearchPagination', () => { }); it('Renders without exploding', async () => { - await renderWithEffects( + await renderInTestApp( { }); it('Define default page limit options', async () => { - await renderWithEffects( + await renderInTestApp( { it('Accept custom page limit label', async () => { const label = 'Page limit:'; - await renderWithEffects( + await renderInTestApp( { }); it('Show the total in text', async () => { - await renderWithEffects( + await renderInTestApp( { }); it('Accept custom page limit text', async () => { - await renderWithEffects( + await renderInTestApp( { }); it('Accept custom page limit options', async () => { - await renderWithEffects( + await renderInTestApp( { }); it('Set page limit in the context', async () => { - await renderWithEffects( + await renderInTestApp( { pageCursor: 'MQ==', // page: 1 }; - await renderWithEffects( + await renderInTestApp( { pageCursor: 'Mg==', // page: 2 }; - await renderWithEffects( + await renderInTestApp( { return Buffer.from(pageCursor.toString(), 'utf-8').toString('base64'); @@ -119,15 +121,18 @@ export type SearchPaginationBaseProps = { * @public */ export const SearchPaginationBase = (props: SearchPaginationBaseProps) => { + const { t } = useTranslationRef(searchReactTranslationRef); const { total: count = -1, cursor: pageCursor, hasNextPage, onCursorChange: onPageCursorChange, limit: rowsPerPage = 25, - limitLabel: labelRowsPerPage = 'Results per page:', + limitLabel: labelRowsPerPage = t('searchPagination.limitLabel'), limitText: labelDisplayedRows = ({ from, to }) => - count > 0 ? `of ${count}` : `${from}-${to}`, + count > 0 + ? t('searchPagination.limitText', { num: `${count}` }) + : `${from}-${to}`, limitOptions: rowsPerPageOptions, onLimitChange: onPageLimitChange, ...rest diff --git a/plugins/search-react/src/components/SearchResult/SearchResult.tsx b/plugins/search-react/src/components/SearchResult/SearchResult.tsx index b0f4570885..99c7f1fb3a 100644 --- a/plugins/search-react/src/components/SearchResult/SearchResult.tsx +++ b/plugins/search-react/src/components/SearchResult/SearchResult.tsx @@ -32,6 +32,8 @@ import { SearchResultListItemExtensions, SearchResultListItemExtensionsProps, } from '../../extensions'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { searchReactTranslationRef } from '../../translation'; /** * Props for {@link SearchResultContext} @@ -186,11 +188,12 @@ export type SearchResultProps = Pick & * @public */ export const SearchResultComponent = (props: SearchResultProps) => { + const { t } = useTranslationRef(searchReactTranslationRef); const { query, children, noResultsComponent = ( - + ), ...rest } = props; diff --git a/plugins/search-react/src/components/SearchResultGroup/SearchResultGroup.tsx b/plugins/search-react/src/components/SearchResultGroup/SearchResultGroup.tsx index 4c2d300314..9a9290bcbc 100644 --- a/plugins/search-react/src/components/SearchResultGroup/SearchResultGroup.tsx +++ b/plugins/search-react/src/components/SearchResultGroup/SearchResultGroup.tsx @@ -51,6 +51,8 @@ import { useSearchResultListItemExtensions } from '../../extensions'; import { DefaultResultListItem } from '../DefaultResultListItem'; import { SearchResultState, SearchResultStateProps } from '../SearchResult'; +import { searchReactTranslationRef } from '../../translation'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; const useStyles = makeStyles((theme: Theme) => ({ listSubheader: { @@ -356,6 +358,7 @@ export function SearchResultGroupLayout( ) { const classes = useStyles(); const [anchorEl, setAnchorEl] = useState(null); + const { t } = useTranslationRef(searchReactTranslationRef); const { error, @@ -365,7 +368,7 @@ export function SearchResultGroupLayout( titleProps = {}, link = ( <> - See all + {t('searchResultGroup.linkTitle')} ), @@ -387,7 +390,7 @@ export function SearchResultGroupLayout( ), disableRenderingWithNoResults, noResultsComponent = disableRenderingWithNoResults ? null : ( - + ), ...rest } = props; @@ -434,7 +437,7 @@ export function SearchResultGroupLayout( component="button" icon={} variant="outlined" - label="Add filter" + label={t('searchResultGroup.addFilterButtonTitle')} aria-controls="filters-menu" aria-haspopup="true" onClick={handleClick} diff --git a/plugins/search-react/src/components/SearchResultList/SearchResultList.tsx b/plugins/search-react/src/components/SearchResultList/SearchResultList.tsx index 48ed8ac92f..a5459d8619 100644 --- a/plugins/search-react/src/components/SearchResultList/SearchResultList.tsx +++ b/plugins/search-react/src/components/SearchResultList/SearchResultList.tsx @@ -30,6 +30,8 @@ import { useSearchResultListItemExtensions } from '../../extensions'; import { DefaultResultListItem } from '../DefaultResultListItem'; import { SearchResultState, SearchResultStateProps } from '../SearchResult'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { searchReactTranslationRef } from '../../translation'; /** * Props for {@link SearchResultListLayout} @@ -72,6 +74,7 @@ export type SearchResultListLayoutProps = ListProps & { * @public */ export const SearchResultListLayout = (props: SearchResultListLayoutProps) => { + const { t } = useTranslationRef(searchReactTranslationRef); const { error, loading, @@ -84,7 +87,7 @@ export const SearchResultListLayout = (props: SearchResultListLayoutProps) => { ), disableRenderingWithNoResults, noResultsComponent = disableRenderingWithNoResults ? null : ( - + ), ...rest } = props; diff --git a/plugins/search-react/src/components/SearchResultPager/SearchResultPager.tsx b/plugins/search-react/src/components/SearchResultPager/SearchResultPager.tsx index 764bb28129..d1dca8bb95 100644 --- a/plugins/search-react/src/components/SearchResultPager/SearchResultPager.tsx +++ b/plugins/search-react/src/components/SearchResultPager/SearchResultPager.tsx @@ -20,6 +20,8 @@ import ArrowBackIosIcon from '@material-ui/icons/ArrowBackIos'; import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos'; import { useSearch } from '../../context'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { searchReactTranslationRef } from '../../translation'; const useStyles = makeStyles(theme => ({ root: { @@ -36,6 +38,7 @@ const useStyles = makeStyles(theme => ({ export const SearchResultPager = () => { const { fetchNextPage, fetchPreviousPage } = useSearch(); const classes = useStyles(); + const { t } = useTranslationRef(searchReactTranslationRef); if (!fetchNextPage && !fetchPreviousPage) { return <>; @@ -49,7 +52,7 @@ export const SearchResultPager = () => { onClick={fetchPreviousPage} startIcon={} > - Previous + {t('searchResultPager.previous')} ); diff --git a/plugins/search-react/src/translation.ts b/plugins/search-react/src/translation.ts new file mode 100644 index 0000000000..6897911949 --- /dev/null +++ b/plugins/search-react/src/translation.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createTranslationRef } from '@backstage/core-plugin-api/alpha'; + +/** + * @alpha + */ +export const searchReactTranslationRef = createTranslationRef({ + id: 'search-react', + messages: { + searchBar: { + title: 'Search', + placeholder: 'Search in {{org}}', + }, + searchFilter: { + allOptionTitle: 'All', + }, + searchPagination: { + limitLabel: 'Results per page:', + limitText: 'of {{num}}', + }, + noResultsDescription: 'Sorry, no results were found', + searchResultGroup: { + linkTitle: 'See All', + addFilterButtonTitle: 'Add filter', + }, + searchResultPager: { + previous: 'Previous', + next: 'Next', + }, + }, +}); diff --git a/plugins/search/report-alpha.api.md b/plugins/search/report-alpha.api.md index b8df4d3b4c..3af0384d89 100644 --- a/plugins/search/report-alpha.api.md +++ b/plugins/search/report-alpha.api.md @@ -15,6 +15,7 @@ import { RouteRef } from '@backstage/frontend-plugin-api'; import { SearchFilterExtensionComponent } from '@backstage/plugin-search-react/alpha'; import { SearchResultItemExtensionComponent } from '@backstage/plugin-search-react/alpha'; import { SearchResultItemExtensionPredicate } from '@backstage/plugin-search-react/alpha'; +import { TranslationRef } from '@backstage/core-plugin-api/alpha'; // @alpha (undocumented) const _default: FrontendPlugin< @@ -247,5 +248,19 @@ export const searchPage: ExtensionDefinition<{ }; }>; +// @alpha (undocumented) +export const searchTranslationRef: TranslationRef< + 'search', + { + readonly 'searchModal.viewFullResults': 'View Full Results'; + readonly 'searchType.tabs.allTitle': 'All'; + readonly 'searchType.allResults': 'All Results'; + readonly 'searchType.accordion.collapse': 'Collapse'; + readonly 'searchType.accordion.allTitle': 'All'; + readonly 'searchType.accordion.numberOfResults': '{{number}} results'; + readonly 'sidebarSearchModal.title': 'Search'; + } +>; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/search/src/alpha.tsx b/plugins/search/src/alpha.tsx index fed0124fa7..5f55b46028 100644 --- a/plugins/search/src/alpha.tsx +++ b/plugins/search/src/alpha.tsx @@ -284,3 +284,6 @@ export default createFrontendPlugin({ root: rootRouteRef, }), }); + +/** @alpha */ +export { searchTranslationRef } from './translation'; diff --git a/plugins/search/src/components/SearchModal/SearchModal.tsx b/plugins/search/src/components/SearchModal/SearchModal.tsx index 454d2eae96..af7e1c6e10 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.tsx @@ -39,6 +39,8 @@ import { useNavigate } from 'react-router-dom'; import { rootRouteRef } from '../../plugin'; import { SearchResultSet } from '@backstage/plugin-search-common'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { searchTranslationRef } from '../../translation'; /** * @public @@ -121,6 +123,7 @@ export const Modal = ({ const navigate = useNavigate(); const { transitions } = useTheme(); const { focusContent } = useContent(); + const { t } = useTranslationRef(searchTranslationRef); const searchRootRoute = useRouteRef(rootRouteRef)(); const searchBarRef = useRef(null); @@ -171,7 +174,7 @@ export const Modal = ({ onClick={handleSearchBarSubmit} disableRipple > - View Full Results + {t('searchModal.viewFullResults')} diff --git a/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx b/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx index 6a124884c0..1245ceed5a 100644 --- a/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx +++ b/plugins/search/src/components/SearchType/SearchType.Accordion.test.tsx @@ -15,8 +15,12 @@ */ import { ReactNode } from 'react'; -import { mockApis, TestApiProvider } from '@backstage/test-utils'; -import { act, render, waitFor } from '@testing-library/react'; +import { + mockApis, + renderInTestApp, + TestApiProvider, +} from '@backstage/test-utils'; +import { act, waitFor } from '@testing-library/react'; import user from '@testing-library/user-event'; import { searchApiRef, @@ -81,7 +85,7 @@ describe('SearchType.Accordion', () => { }; it('should render as expected', async () => { - const { getByText } = render( + const { getByText } = await renderInTestApp( , @@ -103,7 +107,7 @@ describe('SearchType.Accordion', () => { }); it('should set entire types array when a type is selected', async () => { - const { getByText } = render( + const { getByText } = await renderInTestApp( , @@ -115,7 +119,7 @@ describe('SearchType.Accordion', () => { }); it('should reset types array when all is selected', async () => { - const { getByText } = render( + const { getByText } = await renderInTestApp( { }); it('should reset page cursor when a new type is selected', async () => { - const { getByText } = render( + const { getByText } = await renderInTestApp( , @@ -143,7 +147,7 @@ describe('SearchType.Accordion', () => { }); it('should show result counts if enabled', async () => { - const { getAllByText } = render( + const { getAllByText } = await renderInTestApp( ({ icon: { @@ -83,6 +85,7 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => { const searchApi = useApi(searchApiRef); const [expanded, setExpanded] = useState(true); const { defaultValue, name, showCounts, types: givenTypes } = props; + const { t } = useTranslationRef(searchTranslationRef); const toggleExpanded = () => setExpanded(prevState => !prevState); const handleClick = (type: string) => { @@ -103,7 +106,7 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => { const definedTypes = [ { value: '', - name: 'All', + name: t('searchType.accordion.allTitle'), icon: , }, ...givenTypes, @@ -117,7 +120,7 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => { const counts = await Promise.all( definedTypes - .map(t => t.value) + .map(type => type.value) .map(async type => { const { numberOfResults } = await searchApi.query({ term, @@ -130,9 +133,10 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => { return [ type, numberOfResults !== undefined - ? `${ - numberOfResults >= 10000 ? `>10000` : numberOfResults - } results` + ? t('searchType.accordion.numberOfResults', { + number: + numberOfResults >= 10000 ? `>10000` : `${numberOfResults}`, + }) : ' -- ', ]; }), @@ -160,8 +164,8 @@ export const SearchTypeAccordion = (props: SearchTypeAccordionProps) => { IconButtonProps={{ size: 'small' }} > {expanded - ? 'Collapse' - : definedTypes.filter(t => t.value === selected)[0]!.name} + ? t('searchType.accordion.collapse') + : definedTypes.filter(type => type.value === selected)[0]!.name} { }; it('should render as expected', async () => { - const { getByText } = render( + const { getByText } = await renderInTestApp( , @@ -85,7 +89,7 @@ describe('SearchType.Tabs', () => { }); it('should set entire types array when a type is selected', async () => { - const { getByText } = render( + const { getByText } = await renderInTestApp( , @@ -97,7 +101,7 @@ describe('SearchType.Tabs', () => { }); it('should reset types array when all is selected', async () => { - const { getByText } = render( + const { getByText } = await renderInTestApp( { }); it('should reset page cursor when a new type is selected', async () => { - const { getByText } = render( + const { getByText } = await renderInTestApp( , diff --git a/plugins/search/src/components/SearchType/SearchType.Tabs.tsx b/plugins/search/src/components/SearchType/SearchType.Tabs.tsx index f4323da915..ed09ea940a 100644 --- a/plugins/search/src/components/SearchType/SearchType.Tabs.tsx +++ b/plugins/search/src/components/SearchType/SearchType.Tabs.tsx @@ -20,6 +20,8 @@ import Tab from '@material-ui/core/Tab'; import Tabs from '@material-ui/core/Tabs'; import { makeStyles } from '@material-ui/core/styles'; import { Theme } from '@material-ui/core/styles'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { searchTranslationRef } from '../../translation'; const useStyles = makeStyles((theme: Theme) => ({ tabs: { @@ -49,6 +51,7 @@ export const SearchTypeTabs = (props: SearchTypeTabsProps) => { const classes = useStyles(); const { setPageCursor, setTypes, types } = useSearch(); const { defaultValue, types: givenTypes } = props; + const { t } = useTranslationRef(searchTranslationRef); const changeTab = (_: ChangeEvent<{}>, newType: string) => { setTypes(newType !== '' ? [newType] : []); @@ -66,7 +69,7 @@ export const SearchTypeTabs = (props: SearchTypeTabsProps) => { const definedTypes = [ { value: '', - name: 'All', + name: t('searchType.tabs.allTitle'), }, ...givenTypes, ]; diff --git a/plugins/search/src/components/SearchType/SearchType.test.tsx b/plugins/search/src/components/SearchType/SearchType.test.tsx index fd014369f7..231463c9bf 100644 --- a/plugins/search/src/components/SearchType/SearchType.test.tsx +++ b/plugins/search/src/components/SearchType/SearchType.test.tsx @@ -15,14 +15,18 @@ */ import { configApiRef } from '@backstage/core-plugin-api'; -import { render, screen, waitFor } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { SearchContextProvider, searchApiRef, } from '@backstage/plugin-search-react'; import { SearchType } from './SearchType'; -import { mockApis, TestApiProvider } from '@backstage/test-utils'; +import { + mockApis, + renderInTestApp, + TestApiProvider, +} from '@backstage/test-utils'; describe('SearchType', () => { const initialState = { @@ -53,7 +57,7 @@ describe('SearchType', () => { describe('Type Filter', () => { it('Renders field name and values when provided as props', async () => { - render( + await renderInTestApp( { }); it('Renders correctly based on type filter state', async () => { - render( + await renderInTestApp( { }); it('Renders correctly based on type filter defaultValue', async () => { - render( + await renderInTestApp( { }); it('Selecting a value sets type filter state', async () => { - render( + await renderInTestApp( { }); it('Selecting none defaults to empty state', async () => { - render( + await renderInTestApp( ({ label: { @@ -63,6 +65,7 @@ const SearchType = (props: SearchTypeProps) => { const { className, defaultValue, name, values = [] } = props; const classes = useStyles(); const { types, setTypes } = useSearch(); + const { t } = useTranslationRef(searchTranslationRef); useEffectOnce(() => { if (!types.length) { @@ -94,7 +97,7 @@ const SearchType = (props: SearchTypeProps) => { variant="outlined" value={types} onChange={handleChange} - placeholder="All Results" + placeholder={t('searchType.allResults')} renderValue={selected => (
{(selected as string[]).map(value => ( diff --git a/plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx b/plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx index ad687a35db..5dde4f385b 100644 --- a/plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx +++ b/plugins/search/src/components/SidebarSearchModal/SidebarSearchModal.tsx @@ -22,6 +22,8 @@ import { SearchModalProvider, useSearchModal, } from '../SearchModal'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { searchTranslationRef } from '../../translation'; /** * Props for {@link SidebarSearchModal}. @@ -38,13 +40,14 @@ export type SidebarSearchModalProps = Pick< const SidebarSearchModalContent = (props: SidebarSearchModalProps) => { const { state, toggleModal } = useSearchModal(); const Icon = props.icon ? props.icon : SearchIcon; + const { t } = useTranslationRef(searchTranslationRef); return ( <>