1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-22 01:16:07 +02:00

refactor: rename message banners to banners (#5098)

https://linear.app/unleash/issue/2-1531/rename-message-banners-to-banners

This renames "message banners" to "banners".

I also added support for external banners coming from a `banner` flag
instead of only `messageBanner` flag, so we can eventually migrate to
the new one in the future if we want.
This commit is contained in:
Nuno Góis 2023-10-19 13:18:25 +01:00 committed by GitHub
parent 3d9f31f839
commit 957546e305
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 106 additions and 104 deletions

View File

@ -20,8 +20,8 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import MaintenanceBanner from './maintenance/MaintenanceBanner'; import MaintenanceBanner from './maintenance/MaintenanceBanner';
import { styled } from '@mui/material'; import { styled } from '@mui/material';
import { InitialRedirect } from './InitialRedirect'; import { InitialRedirect } from './InitialRedirect';
import { InternalMessageBanners } from './messageBanners/internalMessageBanners/InternalMessageBanners'; import { InternalBanners } from './banners/internalBanners/InternalBanners';
import { ExternalMessageBanners } from './messageBanners/externalMessageBanners/ExternalMessageBanners'; import { ExternalBanners } from './banners/externalBanners/ExternalBanners';
const StyledContainer = styled('div')(() => ({ const StyledContainer = styled('div')(() => ({
'& ul': { '& ul': {
@ -65,8 +65,8 @@ export const App = () => {
)} )}
show={<MaintenanceBanner />} show={<MaintenanceBanner />}
/> />
<ExternalMessageBanners /> <ExternalBanners />
<InternalMessageBanners /> <InternalBanners />
<StyledContainer> <StyledContainer>
<ToastRenderer /> <ToastRenderer />
<Routes> <Routes>

View File

@ -7,10 +7,10 @@ import {
import { styled, Icon, Link } from '@mui/material'; import { styled, Icon, Link } from '@mui/material';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { MessageBannerDialog } from './MessageBannerDialog/MessageBannerDialog'; import { BannerDialog } from './BannerDialog/BannerDialog';
import { useState } from 'react'; import { useState } from 'react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import { BannerVariant, IMessageBanner } from 'interfaces/messageBanner'; import { BannerVariant, IBanner } from 'interfaces/banner';
const StyledBar = styled('aside', { const StyledBar = styled('aside', {
shouldForwardProp: (prop) => prop !== 'variant' && prop !== 'sticky', shouldForwardProp: (prop) => prop !== 'variant' && prop !== 'sticky',
@ -43,11 +43,11 @@ const StyledIcon = styled('div', {
color: theme.palette[variant].main, color: theme.palette[variant].main,
})); }));
interface IMessageBannerProps { interface IBannerProps {
messageBanner: IMessageBanner; banner: IBanner;
} }
export const MessageBanner = ({ messageBanner }: IMessageBannerProps) => { export const Banner = ({ banner }: IBannerProps) => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { const {
@ -60,7 +60,7 @@ export const MessageBanner = ({ messageBanner }: IMessageBannerProps) => {
plausibleEvent, plausibleEvent,
dialogTitle, dialogTitle,
dialog, dialog,
} = messageBanner; } = banner;
return ( return (
<StyledBar variant={variant} sticky={sticky}> <StyledBar variant={variant} sticky={sticky}>
@ -75,13 +75,13 @@ export const MessageBanner = ({ messageBanner }: IMessageBannerProps) => {
> >
{linkText} {linkText}
</BannerButton> </BannerButton>
<MessageBannerDialog <BannerDialog
open={open} open={open}
setOpen={setOpen} setOpen={setOpen}
title={dialogTitle || linkText} title={dialogTitle || linkText}
> >
{dialog!} {dialog!}
</MessageBannerDialog> </BannerDialog>
</StyledBar> </StyledBar>
); );
}; };
@ -127,7 +127,7 @@ const BannerButton = ({
const trackEvent = () => { const trackEvent = () => {
if (!plausibleEvent) return; if (!plausibleEvent) return;
tracker.trackEvent('message_banner', { tracker.trackEvent('banner', {
props: { event: plausibleEvent }, props: { event: plausibleEvent },
}); });
}; };

View File

@ -8,19 +8,19 @@ const StyledReactMarkdown = styled(ReactMarkdown)(({ theme }) => ({
}, },
})); }));
interface IMessageBannerDialogProps { interface IBannerDialogProps {
title: string; title: string;
open: boolean; open: boolean;
setOpen: React.Dispatch<React.SetStateAction<boolean>>; setOpen: React.Dispatch<React.SetStateAction<boolean>>;
children: string; children: string;
} }
export const MessageBannerDialog = ({ export const BannerDialog = ({
open, open,
setOpen, setOpen,
title, title,
children, children,
}: IMessageBannerDialogProps) => { }: IBannerDialogProps) => {
return ( return (
<Dialogue <Dialogue
title={title} title={title}

View File

@ -0,0 +1,30 @@
import { Banner } from 'component/banners/Banner/Banner';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useVariant } from 'hooks/useVariant';
import { IBanner } from 'interfaces/banner';
export const ExternalBanners = () => {
const { uiConfig } = useUiConfig();
const bannerVariantFromMessageBannerFlag = useVariant<IBanner | IBanner[]>(
uiConfig.flags.messageBanner,
);
const bannerVariantFromBannerFlag = useVariant<IBanner | IBanner[]>(
uiConfig.flags.banner,
);
const bannerVariant =
bannerVariantFromMessageBannerFlag || bannerVariantFromBannerFlag || [];
const banners: IBanner[] = Array.isArray(bannerVariant)
? bannerVariant
: [bannerVariant];
return (
<>
{banners.map((banner) => (
<Banner key={banner.message} banner={banner} />
))}
</>
);
};

View File

@ -0,0 +1,14 @@
import { Banner } from 'component/banners/Banner/Banner';
import { useBanners } from 'hooks/api/getters/useBanners/useBanners';
export const InternalBanners = () => {
const { banners } = useBanners();
return (
<>
{banners.map((banner) => (
<Banner key={banner.id} banner={banner} />
))}
</>
);
};

View File

@ -1,28 +0,0 @@
import { MessageBanner } from 'component/messageBanners/MessageBanner/MessageBanner';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useVariant } from 'hooks/useVariant';
import { IMessageBanner } from 'interfaces/messageBanner';
export const ExternalMessageBanners = () => {
const { uiConfig } = useUiConfig();
const messageBannerVariant =
useVariant<IMessageBanner | IMessageBanner[]>(
uiConfig.flags.messageBanner,
) || [];
const messageBanners: IMessageBanner[] = Array.isArray(messageBannerVariant)
? messageBannerVariant
: [messageBannerVariant];
return (
<>
{messageBanners.map((messageBanner) => (
<MessageBanner
key={messageBanner.message}
messageBanner={messageBanner}
/>
))}
</>
);
};

View File

@ -1,17 +0,0 @@
import { MessageBanner } from 'component/messageBanners/MessageBanner/MessageBanner';
import { useMessageBanners } from 'hooks/api/getters/useMessageBanners/useMessageBanners';
export const InternalMessageBanners = () => {
const { messageBanners } = useMessageBanners();
return (
<>
{messageBanners.map((messageBanner) => (
<MessageBanner
key={messageBanner.id}
messageBanner={messageBanner}
/>
))}
</>
);
};

View File

@ -1,27 +1,22 @@
import { IInternalMessageBanner } from 'interfaces/messageBanner'; import { IInternalBanner } from 'interfaces/banner';
import useAPI from '../useApi/useApi'; import useAPI from '../useApi/useApi';
const ENDPOINT = 'api/admin/message-banners'; const ENDPOINT = 'api/admin/banners';
type AddOrUpdateMessageBanner = Omit< type AddOrUpdateBanner = Omit<IInternalBanner, 'id' | 'createdAt'>;
IInternalMessageBanner,
'id' | 'createdAt'
>;
export const useMessageBannersApi = () => { export const useBannersApi = () => {
const { loading, makeRequest, createRequest, errors } = useAPI({ const { loading, makeRequest, createRequest, errors } = useAPI({
propagateErrors: true, propagateErrors: true,
}); });
const addMessageBanner = async ( const addBanner = async (banner: AddOrUpdateBanner) => {
messageBanner: AddOrUpdateMessageBanner, const requestId = 'addBanner';
) => {
const requestId = 'addMessageBanner';
const req = createRequest( const req = createRequest(
ENDPOINT, ENDPOINT,
{ {
method: 'POST', method: 'POST',
body: JSON.stringify(messageBanner), body: JSON.stringify(banner),
}, },
requestId, requestId,
); );
@ -30,16 +25,16 @@ export const useMessageBannersApi = () => {
return response.json(); return response.json();
}; };
const updateMessageBanner = async ( const updateBanner = async (
messageBannerId: number, bannerId: number,
messageBanner: AddOrUpdateMessageBanner, banner: AddOrUpdateBanner,
) => { ) => {
const requestId = 'updateMessageBanner'; const requestId = 'updateBanner';
const req = createRequest( const req = createRequest(
`${ENDPOINT}/${messageBannerId}`, `${ENDPOINT}/${bannerId}`,
{ {
method: 'PUT', method: 'PUT',
body: JSON.stringify(messageBanner), body: JSON.stringify(banner),
}, },
requestId, requestId,
); );
@ -47,10 +42,10 @@ export const useMessageBannersApi = () => {
await makeRequest(req.caller, req.id); await makeRequest(req.caller, req.id);
}; };
const removeMessageBanner = async (messageBannerId: number) => { const removeBanner = async (bannerId: number) => {
const requestId = 'removeMessageBanner'; const requestId = 'removeBanner';
const req = createRequest( const req = createRequest(
`${ENDPOINT}/${messageBannerId}`, `${ENDPOINT}/${bannerId}`,
{ method: 'DELETE' }, { method: 'DELETE' },
requestId, requestId,
); );
@ -59,9 +54,9 @@ export const useMessageBannersApi = () => {
}; };
return { return {
addMessageBanner, addBanner,
updateMessageBanner, updateBanner,
removeMessageBanner, removeBanner,
errors, errors,
loading, loading,
}; };

View File

@ -4,25 +4,24 @@ import handleErrorResponses from '../httpErrorResponseHandler';
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR'; import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
import useUiConfig from '../useUiConfig/useUiConfig'; import useUiConfig from '../useUiConfig/useUiConfig';
import { useUiFlag } from 'hooks/useUiFlag'; import { useUiFlag } from 'hooks/useUiFlag';
import { IInternalMessageBanner } from 'interfaces/messageBanner'; import { IInternalBanner } from 'interfaces/banner';
const ENDPOINT = 'api/admin/message-banners'; const ENDPOINT = 'api/admin/banners';
export const useMessageBanners = () => { export const useBanners = () => {
const { isEnterprise } = useUiConfig(); const { isEnterprise } = useUiConfig();
const internalMessageBanners = useUiFlag('internalMessageBanners'); const bannersEnabled = useUiFlag('banners');
const { data, error, mutate } = useConditionalSWR( const { data, error, mutate } = useConditionalSWR(
isEnterprise() && internalMessageBanners, isEnterprise() && bannersEnabled,
{ messageBanners: [] }, { banners: [] },
formatApiPath(ENDPOINT), formatApiPath(ENDPOINT),
fetcher, fetcher,
); );
return useMemo( return useMemo(
() => ({ () => ({
messageBanners: (data?.messageBanners ?? banners: (data?.banners ?? []) as IInternalBanner[],
[]) as IInternalMessageBanner[],
loading: !error && !data, loading: !error && !data,
refetch: () => mutate(), refetch: () => mutate(),
error, error,
@ -33,6 +32,6 @@ export const useMessageBanners = () => {
const fetcher = (path: string) => { const fetcher = (path: string) => {
return fetch(path) return fetch(path)
.then(handleErrorResponses('Message Banners')) .then(handleErrorResponses('Banners'))
.then((res) => res.json()); .then((res) => res.json());
}; };

View File

@ -15,7 +15,7 @@ export type CustomEvents =
| 'change_request' | 'change_request'
| 'favorite' | 'favorite'
| 'maintenance' | 'maintenance'
| 'message_banner' | 'banner'
| 'hidden_environment' | 'hidden_environment'
| 'project_overview' | 'project_overview'
| 'suggest_tags' | 'suggest_tags'

View File

@ -1,6 +1,6 @@
export type BannerVariant = 'warning' | 'info' | 'error' | 'success'; export type BannerVariant = 'warning' | 'info' | 'error' | 'success';
export interface IMessageBanner { export interface IBanner {
message: string; message: string;
variant?: BannerVariant; variant?: BannerVariant;
sticky?: boolean; sticky?: boolean;
@ -12,7 +12,7 @@ export interface IMessageBanner {
dialog?: string; dialog?: string;
} }
export interface IInternalMessageBanner extends IMessageBanner { export interface IInternalBanner extends IBanner {
id: number; id: number;
enabled: boolean; enabled: boolean;
createdAt: string; createdAt: string;

View File

@ -47,6 +47,7 @@ export type UiFlags = {
embedProxyFrontend?: boolean; embedProxyFrontend?: boolean;
maintenanceMode?: boolean; maintenanceMode?: boolean;
messageBanner?: Variant; messageBanner?: Variant;
banner?: Variant;
featuresExportImport?: boolean; featuresExportImport?: boolean;
caseInsensitiveInOperators?: boolean; caseInsensitiveInOperators?: boolean;
proPlanAutoCharge?: boolean; proPlanAutoCharge?: boolean;
@ -69,7 +70,7 @@ export type UiFlags = {
accessOverview?: boolean; accessOverview?: boolean;
datadogJsonTemplate?: boolean; datadogJsonTemplate?: boolean;
dependentFeatures?: boolean; dependentFeatures?: boolean;
internalMessageBanners?: boolean; banners?: boolean;
disableEnvsOnRevive?: boolean; disableEnvsOnRevive?: boolean;
playgroundImprovements?: boolean; playgroundImprovements?: boolean;
}; };

View File

@ -77,6 +77,7 @@ exports[`should create default config 1`] = `
"flags": { "flags": {
"accessOverview": false, "accessOverview": false,
"anonymiseEventLog": false, "anonymiseEventLog": false,
"banners": false,
"caseInsensitiveInOperators": false, "caseInsensitiveInOperators": false,
"customRootRolesKillSwitch": false, "customRootRolesKillSwitch": false,
"datadogJsonTemplate": false, "datadogJsonTemplate": false,
@ -93,7 +94,6 @@ exports[`should create default config 1`] = `
"featuresExportImport": true, "featuresExportImport": true,
"filterInvalidClientMetrics": false, "filterInvalidClientMetrics": false,
"googleAuthEnabled": false, "googleAuthEnabled": false,
"internalMessageBanners": false,
"lastSeenByEnvironment": false, "lastSeenByEnvironment": false,
"maintenanceMode": false, "maintenanceMode": false,
"messageBanner": { "messageBanner": {
@ -122,6 +122,7 @@ exports[`should create default config 1`] = `
"experiments": { "experiments": {
"accessOverview": false, "accessOverview": false,
"anonymiseEventLog": false, "anonymiseEventLog": false,
"banners": false,
"caseInsensitiveInOperators": false, "caseInsensitiveInOperators": false,
"customRootRolesKillSwitch": false, "customRootRolesKillSwitch": false,
"datadogJsonTemplate": false, "datadogJsonTemplate": false,
@ -138,7 +139,6 @@ exports[`should create default config 1`] = `
"featuresExportImport": true, "featuresExportImport": true,
"filterInvalidClientMetrics": false, "filterInvalidClientMetrics": false,
"googleAuthEnabled": false, "googleAuthEnabled": false,
"internalMessageBanners": false,
"lastSeenByEnvironment": false, "lastSeenByEnvironment": false,
"maintenanceMode": false, "maintenanceMode": false,
"messageBanner": { "messageBanner": {

View File

@ -34,8 +34,7 @@ export type IFlagKey =
| 'datadogJsonTemplate' | 'datadogJsonTemplate'
| 'disableMetrics' | 'disableMetrics'
| 'useLastSeenRefactor' | 'useLastSeenRefactor'
| 'internalMessageBanners' | 'banners'
| 'internalMessageBanner'
| 'separateAdminClientApi' | 'separateAdminClientApi'
| 'disableEnvsOnRevive' | 'disableEnvsOnRevive'
| 'playgroundImprovements'; | 'playgroundImprovements';
@ -162,8 +161,8 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_USE_LAST_SEEN_REFACTOR, process.env.UNLEASH_EXPERIMENTAL_USE_LAST_SEEN_REFACTOR,
false, false,
), ),
internalMessageBanners: parseEnvVarBoolean( banners: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_INTERNAL_MESSAGE_BANNERS, process.env.UNLEASH_EXPERIMENTAL_BANNERS,
false, false,
), ),
separateAdminClientApi: parseEnvVarBoolean( separateAdminClientApi: parseEnvVarBoolean(

View File

@ -0,0 +1,9 @@
'use strict';
exports.up = function (db, cb) {
db.runSql(`ALTER TABLE message_banners RENAME TO banners`, cb);
};
exports.down = function (db, cb) {
db.runSql(`ALTER TABLE banners RENAME TO message_banners`, cb);
};