Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add a notification banner to display for first time users #3396

Open
wants to merge 24 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2a7a2e3
WIP: set basics for the Notification banner
musale Oct 29, 2024
bd5a321
Revert to previous functionality
musale Oct 30, 2024
f26b351
Use v8 styling construct and a MessageBar
musale Oct 30, 2024
fb0096e
Merge branch 'dev' into feat/notification-banner
musale Oct 31, 2024
377f689
Use fluent v9 and get messages for notification from GE.json
musale Oct 31, 2024
06c88b3
Add fluent v9 components dependencies
musale Oct 31, 2024
daf6010
Update styles and components for Notification
musale Oct 31, 2024
31949f8
Add text for notification
musale Oct 31, 2024
bda0a91
Hook theming of v9 provider to prop value
musale Oct 31, 2024
e248e4a
Track the tutorial link with telemetry
musale Nov 5, 2024
2ef65a5
Add slice banner
musale Nov 5, 2024
39814b6
Persist banner state to localstorage
musale Nov 5, 2024
24fa979
Update state usage of banner state
musale Nov 5, 2024
1d80997
Use local storage to track banner visibility
musale Nov 5, 2024
0ec9823
Add the background and custom types
musale Nov 5, 2024
f630d65
Fix theming and sizing of text in the body
musale Nov 6, 2024
8891992
Handle dismissing of the banner
musale Nov 6, 2024
460aacd
Lock the v9 packages version
musale Nov 6, 2024
b90a2aa
Update the banner notification link
musale Nov 18, 2024
ad6b7da
Merge remote-tracking branch 'origin' into feat/notification-banner
musale Nov 18, 2024
8e4824f
Enhance type safety for trackReactComponent method
musale Nov 18, 2024
7244c75
Add telemetry tracking for Notification component
musale Nov 18, 2024
fa8fd1c
Refactor Notification import and update App component structure
musale Nov 18, 2024
03eae38
Add telemetry tracking for notification dismiss button
musale Nov 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,569 changes: 1,558 additions & 11 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"dependencies": {
"@augloop/types-core": "file:packages/types-core-2.16.189.tgz",
"@axe-core/webdriverjs": "4.10.0",
"@fluentui/react-components": "9.55.1",
"@azure/msal-browser": "3.26.1",
"@babel/core": "7.26.0",
"@babel/runtime": "7.26.0",
Expand Down
3 changes: 2 additions & 1 deletion src/app/middleware/localStorageMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { CURRENT_THEME } from '../services/graph-constants';
import { getUniquePaths } from '../services/reducers/collections-reducer.util';
import {
CHANGE_THEME_SUCCESS, COLLECTION_CREATE_SUCCESS,
RESOURCEPATHS_ADD_SUCCESS, RESOURCEPATHS_DELETE_SUCCESS, SAMPLES_FETCH_SUCCESS
RESOURCEPATHS_ADD_SUCCESS, RESOURCEPATHS_DELETE_SUCCESS,
SAMPLES_FETCH_SUCCESS
} from '../services/redux-constants';
import { saveToLocalStorage } from '../utils/local-storage';

Expand Down
3 changes: 2 additions & 1 deletion src/app/services/graph-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ export const ADMIN_CONSENT_DOC_LINK = 'https://learn.microsoft.com/en-us/graph/s
// eslint-disable-next-line max-len
export const CONSENT_TYPE_DOC_LINK = 'https://learn.microsoft.com/en-us/graph/api/resources/oauth2permissiongrant?view=graph-rest-1.0#:~:text=(eq%20only).-,consentType,-String'
export const CURRENT_THEME='CURRENT_THEME';
export const EXP_URL='https://default.exp-tas.com/exptas76/9b835cbf-9742-40db-84a7-7a323a77f3eb-gedev/api/v1/tas'
export const EXP_URL='https://default.exp-tas.com/exptas76/9b835cbf-9742-40db-84a7-7a323a77f3eb-gedev/api/v1/tas'
musale marked this conversation as resolved.
Show resolved Hide resolved
export const BANNER_IS_VISIBLE = 'bannerIsVisible';
1 change: 1 addition & 0 deletions src/app/services/redux-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,4 @@ export const REVOKE_SCOPES_PENDING = 'auth/revokeScopes/pending';
export const REVOKE_SCOPES_SUCCESS = 'auth/revokeScopes/fulfilled';
export const REVOKE_SCOPES_ERROR = 'auth/revokeScopes/rejected';
export const COLLECTION_CREATE_SUCCESS = 'collections/createCollection';
export const SET_BANNER_STATE = 'banner/setBannerState';
185 changes: 101 additions & 84 deletions src/app/views/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Announced, getTheme, ITheme, styled } from '@fluentui/react';
import { FluentProvider, teamsHighContrastTheme, Theme, webDarkTheme, webLightTheme } from '@fluentui/react-components';
import { bindActionCreators, Dispatch } from '@reduxjs/toolkit';
import { Resizable } from 're-resizable';
import { Component } from 'react';
Expand Down Expand Up @@ -30,6 +31,7 @@ import { TermsOfUseMessage } from './app-sections';
import { headerMessaging } from './app-sections/HeaderMessaging';
import { appStyles } from './App.styles';
import { classNames } from './classnames';
import Notification from './common/banners/Notification';
import { KeyboardCopyEvent } from './common/copy-button/KeyboardCopyEvent';
import { StatusMessages } from './common/lazy-loader/component-registry';
import PopupsWrapper from './common/popups/PopupsWrapper';
Expand All @@ -43,6 +45,7 @@ export interface IAppProps {
theme?: ITheme;
styles?: object;
profile: object;
appTheme: string;
graphExplorerMode: Mode;
sidebarProperties: ISidebarProps;
sampleQuery: IQuery;
Expand Down Expand Up @@ -404,97 +407,111 @@ class App extends Component<IAppProps, IAppState> {
this.removeFlexBasisProperty();
this.removeSidebarHeightProperty();

const fluentV9Themes: Record<string, Theme>= {
'light': webLightTheme,
'dark': webDarkTheme,
'high-contrast': teamsHighContrastTheme
}
return (
// @ts-ignore
<ThemeContext.Provider value={this.props.appTheme}>
<PopupsProvider>
<div className={`ms-Grid ${classes.app}`} style={{ paddingLeft: mobileScreen && '15px' }}>
<MainHeader
toggleSidebar={this.toggleSidebar}
/>
<Announced
message={
!showSidebar
? translateMessage('Sidebar minimized')
: translateMessage('Sidebar maximized')
}
/>
<div className={`ms-Grid-row ${classes.appRow}`} style={{
flexWrap: mobileScreen && 'wrap',
marginRight: showSidebar || (graphExplorerMode === Mode.TryIt) && '-20px',
flexDirection: (graphExplorerMode === Mode.TryIt) ? 'column' : 'row'
}}>
{graphExplorerMode === Mode.Complete && (
<Resizable
onResize={(e: any, direction: any, ref: any) => {
if (ref?.style?.width) {
this.resizeSideBar(ref.style.width);
}
}}
className={`ms-Grid-col ms-sm12 ms-md4 ms-lg4 ${sidebarWidth} resizable-sidebar`}
minWidth={'71'}
maxWidth={maxWidth}
enable={{
right: true
}}
handleClasses={{
right: classes.vResizeHandle
}}
bounds={'parent'}
size={{
width: sideWidth,
height: ''
}}
>
<Sidebar currentTab={this.state.sidebarTabSelection}
setSidebarTabSelection={this.setSidebarTabSelection} showSidebar={showSidebar}
toggleSidebar={this.toggleSidebar}
mobileScreen={mobileScreen} />
</Resizable>
)}
{graphExplorerMode === Mode.TryIt &&
<FluentProvider theme={fluentV9Themes[this.props.appTheme]}>
<ThemeContext.Provider value={this.props.appTheme}>
<PopupsProvider>
<div className={`ms-Grid ${classes.app}`} style={{ paddingLeft: mobileScreen && '15px' }}>
<MainHeader
toggleSidebar={this.toggleSidebar}
/>
<Announced
message={
!showSidebar
? translateMessage('Sidebar minimized')
: translateMessage('Sidebar maximized')
}
/>
<div className={`ms-Grid-row ${classes.appRow}`} style={{
flexWrap: mobileScreen && 'wrap',
marginRight: showSidebar || (graphExplorerMode === Mode.TryIt) && '-20px',
flexDirection: (graphExplorerMode === Mode.TryIt) ? 'column' : 'row'
}}>
{graphExplorerMode === Mode.Complete && (
<Resizable
onResize={(e: any, direction: any, ref: any) => {
if (ref?.style?.width) {
this.resizeSideBar(ref.style.width);
}
}}
className={`ms-Grid-col ms-sm12 ms-md4 ms-lg4 ${sidebarWidth} resizable-sidebar`}
minWidth={'71'}
maxWidth={maxWidth}
enable={{
right: true
}}
handleClasses={{
right: classes.vResizeHandle
}}
bounds={'parent'}
size={{
width: sideWidth,
height: ''
}}
>
<Sidebar currentTab={this.state.sidebarTabSelection}
setSidebarTabSelection={this.setSidebarTabSelection} showSidebar={showSidebar}
toggleSidebar={this.toggleSidebar}
mobileScreen={mobileScreen} />
</Resizable>
)}
{graphExplorerMode === Mode.TryIt &&
headerMessaging(query)}

{displayContent && (
<Resizable
bounds={'window'}
className={`ms-Grid-col ms-sm12 ms-md4 ms-lg4 ${layout}`}
enable={{
right: false
}}
size={{
width: graphExplorerMode === Mode.TryIt ? '100%' : contentWidth,
height: ''
}}
style={!sidebarProperties.showSidebar && !mobileScreen ? {
marginLeft: '8px', display: 'flex', flexDirection: 'column', alignItems: 'stretch', flex: 1
} : {
display: 'flex', flexDirection: 'column', alignItems: 'stretch', flex: 1
}}
>
<ValidationProvider>
<div style={{ marginBottom: 2 }} >
<QueryRunner onSelectVerb={this.handleSelectVerb} />
</div>
<div style={{
{displayContent && (
<Resizable
bounds={'window'}
className={`ms-Grid-col ms-sm12 ms-md4 ms-lg4 ${layout}`}
enable={{
right: false
}}
size={{
width: graphExplorerMode === Mode.TryIt ? '100%' : contentWidth,
height: ''
}}
style={!sidebarProperties.showSidebar && !mobileScreen ? {
marginLeft: '8px', display: 'flex', flexDirection: 'column', alignItems: 'stretch', flex: 1
} : {
display: 'flex', flexDirection: 'column', alignItems: 'stretch', flex: 1
}}>
<div style={mobileScreen ? this.statusAreaMobileStyle : this.statusAreaFullScreenStyle}>
<StatusMessages />
</div>
<QueryResponse />
}}
>
<div className='ms-Grid-row'>
<Notification
header={translateMessage('Banner notification 1 header')}
content={translateMessage('Banner notification 1 content')}
link={translateMessage('Banner notificatication 1 link')}
linkText={translateMessage('Banner notification 1 link text')}/>
</div>
</ValidationProvider>
</Resizable>
)}
</div>
<div style={mobileScreen ? this.statusAreaMobileStyle : this.statusAreaFullScreenStyle}>
<TermsOfUseMessage />
<ValidationProvider>
<div style={{ marginBottom: 2 }} >
<QueryRunner onSelectVerb={this.handleSelectVerb} />
</div>
<div style={{
display: 'flex', flexDirection: 'column', alignItems: 'stretch', flex: 1
}}>
<div style={mobileScreen ? this.statusAreaMobileStyle : this.statusAreaFullScreenStyle}>
<StatusMessages />
</div>
<QueryResponse />
</div>
</ValidationProvider>
</Resizable>
)}
</div>
<div style={mobileScreen ? this.statusAreaMobileStyle : this.statusAreaFullScreenStyle}>
<TermsOfUseMessage />
</div>
</div>
</div>
<PopupsWrapper />
</PopupsProvider>
</ThemeContext.Provider>
<PopupsWrapper />
</PopupsProvider>
</ThemeContext.Provider>
</FluentProvider>
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/views/classnames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ interface IClassNames {
export function classNames({ styles, theme }: IClassNames): any {
const getClassNames = classNamesFunction();
return getClassNames(styles, theme);
}
}
31 changes: 31 additions & 0 deletions src/app/views/common/banners/Notification.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { makeStyles } from '@fluentui/react-components';
import polygons from './bgPolygons.svg';

export const useNotificationStyles = makeStyles({
container: {
padding: '8px',
marginBottom: '8px',
backgroundImage: `url(${polygons})`,
backgroundRepeat: 'no-repeat',
backgroundSize: 'contain',
backgroundPosition: 'right',
'&light': {
backgroundColor: '#E8EFFF',
color: '#000000'
},
'&.dark': {
backgroundColor: '#1D202A',
color: '#ffffff'
},
'&.highContrast': {
backgroundColor: '#0C3B5E',
color: '#ffffff'
}
},
body: {
width: '100%',
'@media (min-width: 720px)': {
width: '70%'
}
}
});
71 changes: 71 additions & 0 deletions src/app/views/common/banners/Notification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {
Button,
Link,
MessageBar,
MessageBarActions,
MessageBarBody,
MessageBarTitle
} from '@fluentui/react-components';
import { DismissRegular, OpenRegular } from '@fluentui/react-icons';
import { useState } from 'react';
import { useAppSelector } from '../../../../store';
import { componentNames, eventTypes, telemetry } from '../../../../telemetry';
import { BANNER_IS_VISIBLE } from '../../../services/graph-constants';
import { translateMessage } from '../../../utils/translate-messages';
import { useNotificationStyles } from './Notification.styles';

interface NotificationProps {
header: string;
content: string;
link: string;
linkText: string;
}

const handleOnClickLink = (e: React.MouseEvent<HTMLAnchorElement>)=>{
telemetry.trackLinkClickEvent(
(e.currentTarget as HTMLAnchorElement).href, componentNames.GRAPH_EXPLORER_TUTORIAL_LINK)
}

const Notification: React.FunctionComponent<NotificationProps> = (props: NotificationProps) => {
const styles = useNotificationStyles();
const storageBanner = localStorage.getItem(BANNER_IS_VISIBLE);
const [isVisible, setIsVisible] = useState(storageBanner === null || storageBanner === 'true');
const theme = useAppSelector(s => s.theme);

const handleDismiss = () => {
telemetry.trackEvent(eventTypes.BUTTON_CLICK_EVENT, {
ComponentName: componentNames.NOTIFICATION_BANNER_DISMISS_BUTTON
});
localStorage.setItem(BANNER_IS_VISIBLE, 'false');
setIsVisible(false);
};

if (!isVisible) {
return null;
}

return (
<MessageBar className={`${styles.container} ${theme}`} icon={''}>
<MessageBarBody className={styles.body}>
<MessageBarTitle>{props.header}</MessageBarTitle><br></br>
{props.content}{' '}
<Link
onClick={handleOnClickLink}
href={props.link}
target='_blank'>{props.linkText} <OpenRegular /></Link>
</MessageBarBody>
<MessageBarActions
containerAction={
<Button
onClick={handleDismiss}
aria-label={translateMessage('Dismiss banner')}
appearance="transparent"
icon={<DismissRegular />}
/>
}
/>
</MessageBar>
);
};

export default telemetry.trackReactComponent(Notification, componentNames.NOTIFICATION_COMPONENT)
Loading
Loading