From f4f50d8eef222bd07cff48d27816ec0b1ec979f8 Mon Sep 17 00:00:00 2001 From: "antonio.bergas" Date: Sat, 18 Mar 2023 23:01:03 +0100 Subject: [PATCH] [Playlist] feat: added dynamic group noun through the config Signed-off-by: antonio.bergas --- app-config.yaml | 4 ++ plugins/playlist/config.d.ts | 31 +++++++++++ plugins/playlist/package.json | 23 +++++++- .../CreatePlaylistButton.tsx | 14 +++-- .../EntityPlaylistDialog.tsx | 38 +++++++++++--- .../PlaylistEditDialog/PlaylistEditDialog.tsx | 13 +++-- .../PlaylistIndexPage/PlaylistIndexPage.tsx | 52 ++++++++++--------- .../components/PlaylistList/PlaylistList.tsx | 7 ++- .../PlaylistPage/PlaylistEntitiesTable.tsx | 10 ++-- .../PlaylistPage/PlaylistHeader.tsx | 5 +- plugins/playlist/src/hooks/index.ts | 1 + plugins/playlist/src/hooks/useConfig.ts | 27 ++++++++++ .../playlist/src/hooks/usePlaylistList.tsx | 2 +- 13 files changed, 181 insertions(+), 46 deletions(-) create mode 100644 plugins/playlist/config.d.ts create mode 100644 plugins/playlist/src/hooks/useConfig.ts diff --git a/app-config.yaml b/app-config.yaml index 385c9c2e0e..7b94e2fb2e 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -125,6 +125,10 @@ proxy: organization: name: My Company + +playlist: + groupPluralNoun: Collections + groupSingularNoun: Collection # Reference documentation http://backstage.io/docs/features/techdocs/configuration # Note: After experimenting with basic setup, use CI/CD to generate docs diff --git a/plugins/playlist/config.d.ts b/plugins/playlist/config.d.ts new file mode 100644 index 0000000000..5f59ffc236 --- /dev/null +++ b/plugins/playlist/config.d.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2020 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. + */ + +export interface Config { + playlist: { + /** + * (Optional) The plural noun for the entities grouping that will shown in the UI; leave empty for `PLaylists`. + * @visibility frontend + */ + groupPluralNoun: string; + + /** + * (Optional) The singular noun for the entities grouping that will shown in the UI; leave empty for `PLaylists`. + * @visibility frontend + */ + groupSingularNoun: string; + }; +} diff --git a/plugins/playlist/package.json b/plugins/playlist/package.json index 59cde3f246..bed1d654c5 100644 --- a/plugins/playlist/package.json +++ b/plugins/playlist/package.json @@ -66,5 +66,26 @@ }, "files": [ "dist" - ] + ], + "configSchema": { + "$schema": "https://backstage.io/schema/config-v1", + "type": "object", + "properties": { + "playlist": { + "type": "object", + "properties": { + "groupPluralNoun": { + "type": "string", + "description": "Frontend plural entities grouping name", + "visibility": "frontend" + }, + "groupSingularNoun": { + "type": "string", + "description": "Frontend singular entities grouping name", + "visibility": "frontend" + } + } + } + } + } } diff --git a/plugins/playlist/src/components/CreatePlaylistButton/CreatePlaylistButton.tsx b/plugins/playlist/src/components/CreatePlaylistButton/CreatePlaylistButton.tsx index c064f28591..a615e2cb36 100644 --- a/plugins/playlist/src/components/CreatePlaylistButton/CreatePlaylistButton.tsx +++ b/plugins/playlist/src/components/CreatePlaylistButton/CreatePlaylistButton.tsx @@ -14,7 +14,12 @@ * limitations under the License. */ -import { errorApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { + configApiRef, + errorApiRef, + useApi, + useRouteRef, +} from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; import { usePermission } from '@backstage/plugin-permission-react'; import { @@ -29,6 +34,7 @@ import { useNavigate } from 'react-router-dom'; import { playlistApiRef } from '../../api'; import { playlistRouteRef } from '../../routes'; import { PlaylistEditDialog } from '../PlaylistEditDialog'; +import { useGroupNoun } from '../../hooks/useConfig'; export const CreatePlaylistButton = () => { const navigate = useNavigate(); @@ -55,13 +61,15 @@ export const CreatePlaylistButton = () => { [errorApi, navigate, playlistApi, playlistRoute], ); + const groupSingularNounLowerCase = useGroupNoun(false, false); + return ( <> {isXSScreen ? ( setOpenDialog(true)} > @@ -74,7 +82,7 @@ export const CreatePlaylistButton = () => { color="primary" onClick={() => setOpenDialog(true)} > - Create Playlist + Create {groupSingularNounLowerCase} )} { [playlistApi], ); + const groupSingularNoun = useGroupNoun(true, false); + const groupSingularNounLowerCase = useGroupNoun(false, true); + const groupPluralNounLowerCase = useGroupNoun(true, true); + useEffect(() => { if (open) { loadPlaylists(); @@ -120,12 +130,19 @@ export const EntityPlaylistDialog = (props: EntityPlaylistDialogProps) => { navigate(playlistRoute({ playlistId })); } catch (e) { alertApi.post({ - message: `Failed to add entity to playlist: ${e}`, + message: `Failed to add entity to ${groupSingularNounLowerCase}: ${e}`, severity: 'error', }); } }, - [alertApi, entity, navigate, playlistApi, playlistRoute], + [ + alertApi, + entity, + navigate, + playlistApi, + playlistRoute, + groupSingularNounLowerCase, + ], ); const [{ loading: addEntityLoading }, addToPlaylist] = useAsyncFn( @@ -141,12 +158,12 @@ export const EntityPlaylistDialog = (props: EntityPlaylistDialogProps) => { }); } catch (e) { alertApi.post({ - message: `Failed to add entity to playlist: ${e}`, + message: `Failed to add entity to ${groupSingularNounLowerCase}: ${e}`, severity: 'error', }); } }, - [alertApi, closeDialog, entity, playlistApi], + [alertApi, closeDialog, entity, playlistApi, groupSingularNounLowerCase], ); return ( @@ -160,7 +177,7 @@ export const EntityPlaylistDialog = (props: EntityPlaylistDialogProps) => { > {(loading || addEntityLoading) && } - Add to Playlist + Add to {groupSingularNoun} { {error && ( - + )} {playlists && entity && ( @@ -205,7 +225,9 @@ export const EntityPlaylistDialog = (props: EntityPlaylistDialogProps) => { - + )} {playlists diff --git a/plugins/playlist/src/components/PlaylistEditDialog/PlaylistEditDialog.tsx b/plugins/playlist/src/components/PlaylistEditDialog/PlaylistEditDialog.tsx index 27cc13fede..ebdf4a1798 100644 --- a/plugins/playlist/src/components/PlaylistEditDialog/PlaylistEditDialog.tsx +++ b/plugins/playlist/src/components/PlaylistEditDialog/PlaylistEditDialog.tsx @@ -15,7 +15,11 @@ */ import { parseEntityRef } from '@backstage/catalog-model'; -import { identityApiRef, useApi } from '@backstage/core-plugin-api'; +import { + configApiRef, + identityApiRef, + useApi, +} from '@backstage/core-plugin-api'; import { humanizeEntityRef } from '@backstage/plugin-catalog-react'; import { PlaylistMetadata } from '@backstage/plugin-playlist-common'; import { @@ -39,6 +43,7 @@ import React from 'react'; import { useForm, Controller } from 'react-hook-form'; import useAsync from 'react-use/lib/useAsync'; import useAsyncFn from 'react-use/lib/useAsyncFn'; +import { useGroupNoun } from '../../hooks/useConfig'; const useStyles = makeStyles({ buttonWrapper: { @@ -105,6 +110,8 @@ export const PlaylistEditDialog = ({ } }; + const groupSingularNounLowercase = useGroupNoun(false, false); + return ( @@ -121,7 +128,7 @@ export const PlaylistEditDialog = ({ fullWidth label="Name" margin="dense" - placeholder="Give your playlist name" + placeholder={`Give your ${groupSingularNounLowercase} name`} required type="text" /> @@ -139,7 +146,7 @@ export const PlaylistEditDialog = ({ label="Description" margin="dense" multiline - placeholder="Describe your playlist" + placeholder={`Describe your ${groupSingularNounLowercase}`} type="text" /> )} diff --git a/plugins/playlist/src/components/PlaylistIndexPage/PlaylistIndexPage.tsx b/plugins/playlist/src/components/PlaylistIndexPage/PlaylistIndexPage.tsx index 0ee829018a..6a92718df2 100644 --- a/plugins/playlist/src/components/PlaylistIndexPage/PlaylistIndexPage.tsx +++ b/plugins/playlist/src/components/PlaylistIndexPage/PlaylistIndexPage.tsx @@ -23,7 +23,7 @@ import { } from '@backstage/core-components'; import { CatalogFilterLayout } from '@backstage/plugin-catalog-react'; -import { PlaylistListProvider } from '../../hooks'; +import { PlaylistListProvider, useGroupNoun } from '../../hooks'; import { CreatePlaylistButton } from '../CreatePlaylistButton'; import { PersonalListPicker } from '../PersonalListPicker'; import { PlaylistList } from '../PlaylistList'; @@ -31,26 +31,30 @@ import { PlaylistOwnerPicker } from '../PlaylistOwnerPicker'; import { PlaylistSearchBar } from '../PlaylistSearchBar'; import { PlaylistSortPicker } from '../PlaylistSortPicker'; -export const PlaylistIndexPage = () => ( - - - - - - - - - - - - - - - - - - - - - -); +export const PlaylistIndexPage = () => { + const groupPluralNoun = useGroupNoun(true, false); + + return ( + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/plugins/playlist/src/components/PlaylistList/PlaylistList.tsx b/plugins/playlist/src/components/PlaylistList/PlaylistList.tsx index fcc3971542..fd8569e758 100644 --- a/plugins/playlist/src/components/PlaylistList/PlaylistList.tsx +++ b/plugins/playlist/src/components/PlaylistList/PlaylistList.tsx @@ -23,18 +23,21 @@ import { } from '@backstage/core-components'; import { Typography } from '@material-ui/core'; -import { usePlaylistList } from '../../hooks'; +import { useGroupNoun, usePlaylistList } from '../../hooks'; import { PlaylistCard } from '../PlaylistCard'; export const PlaylistList = () => { const { loading, error, playlists } = usePlaylistList(); + const groupPluralNounLowercase = useGroupNoun(true, true); return ( <> {loading && } {error && ( - + {error.message} )} diff --git a/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx b/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx index f499f59a61..384d51612b 100644 --- a/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx +++ b/plugins/playlist/src/components/PlaylistPage/PlaylistEntitiesTable.tsx @@ -21,7 +21,7 @@ import { Table, TableFilter, } from '@backstage/core-components'; -import { errorApiRef, useApi } from '@backstage/core-plugin-api'; +import { configApiRef, errorApiRef, useApi } from '@backstage/core-plugin-api'; import { EntityRefLink } from '@backstage/plugin-catalog-react'; import { usePermission } from '@backstage/plugin-permission-react'; import { permissions } from '@backstage/plugin-playlist-common'; @@ -32,6 +32,7 @@ import React, { forwardRef, useCallback, useEffect, useState } from 'react'; import useAsyncFn from 'react-use/lib/useAsyncFn'; import { playlistApiRef } from '../../api'; +import { useGroupNoun } from '../../hooks/useConfig'; import { AddEntitiesDrawer } from './AddEntitiesDrawer'; export const PlaylistEntitiesTable = ({ @@ -41,6 +42,7 @@ export const PlaylistEntitiesTable = ({ }) => { const errorApi = useApi(errorApiRef); const playlistApi = useApi(playlistApiRef); + const configApi = useApi(configApiRef); const [openAddEntitiesDrawer, setOpenAddEntitiesDrawer] = useState(false); const { allowed: editAllowed } = usePermission({ @@ -84,16 +86,18 @@ export const PlaylistEntitiesTable = ({ [errorApi, loadEntities, playlistApi, playlistId], ); + const groupSingularNounLowerCase = useGroupNoun(false, true); + const actions = editAllowed ? [ { icon: DeleteIcon, - tooltip: 'Remove from playlist', + tooltip: `Remove from ${groupSingularNounLowerCase}`, onClick: removeEntity, }, { icon: AddBoxIcon, - tooltip: 'Add entities to playlist', + tooltip: `Add entities to ${groupSingularNounLowerCase}`, isFreeAction: true, onClick: () => setOpenAddEntitiesDrawer(true), }, diff --git a/plugins/playlist/src/components/PlaylistPage/PlaylistHeader.tsx b/plugins/playlist/src/components/PlaylistPage/PlaylistHeader.tsx index 7f4457062d..ecf443b565 100644 --- a/plugins/playlist/src/components/PlaylistPage/PlaylistHeader.tsx +++ b/plugins/playlist/src/components/PlaylistPage/PlaylistHeader.tsx @@ -45,6 +45,7 @@ import useAsyncFn from 'react-use/lib/useAsyncFn'; import { playlistApiRef } from '../../api'; import { rootRouteRef } from '../../routes'; import { PlaylistEditDialog } from '../PlaylistEditDialog'; +import { useGroupNoun } from '../../hooks/useConfig'; const useStyles = makeStyles({ buttonWrapper: { @@ -109,6 +110,8 @@ export const PlaylistHeader = ({ playlist, onUpdate }: PlaylistHeaderProps) => { } }, [playlistApi]); + const groupSingularNoun = useGroupNoun(false, false); + return (
{ onClick: () => setOpenEditDialog(true), }, { - label: 'Delete Playlist', + label: `Delete ${groupSingularNoun}`, icon: , disabled: !deleteAllowed, onClick: () => setOpenDeleteDialog(true), diff --git a/plugins/playlist/src/hooks/index.ts b/plugins/playlist/src/hooks/index.ts index 316121b515..da045fbf65 100644 --- a/plugins/playlist/src/hooks/index.ts +++ b/plugins/playlist/src/hooks/index.ts @@ -15,3 +15,4 @@ */ export * from './usePlaylistList'; +export * from './useConfig'; diff --git a/plugins/playlist/src/hooks/useConfig.ts b/plugins/playlist/src/hooks/useConfig.ts new file mode 100644 index 0000000000..15d44570a8 --- /dev/null +++ b/plugins/playlist/src/hooks/useConfig.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2022 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 { configApiRef, useApi } from '@backstage/core-plugin-api'; + +export function useGroupNoun(inPlural: boolean, inLowerCase: boolean) { + const configApi = useApi(configApiRef); + const defaultNoun = inPlural ? 'Playlists' : 'Playlist'; + const configProp = inPlural ? 'groupPluralNoun' : 'groupSingularNoun'; + const groupNoun = + configApi.getOptionalString(`playlist.${configProp}`) ?? `${defaultNoun}`; + + return inLowerCase ? groupNoun.toLocaleLowerCase('en-US') : groupNoun; +} diff --git a/plugins/playlist/src/hooks/usePlaylistList.tsx b/plugins/playlist/src/hooks/usePlaylistList.tsx index 69f712413b..4d2b857520 100644 --- a/plugins/playlist/src/hooks/usePlaylistList.tsx +++ b/plugins/playlist/src/hooks/usePlaylistList.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { useApi } from '@backstage/core-plugin-api'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; import { Playlist } from '@backstage/plugin-playlist-common'; import { compact, isEqual } from 'lodash'; import qs from 'qs';