Skip to content

Commit

Permalink
Add authentication change announcement
Browse files Browse the repository at this point in the history
Add announcement to header to notify users of upcoming authentication change
  • Loading branch information
paustint committed Aug 30, 2024
1 parent bed62ec commit 50bbd13
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 12 deletions.
22 changes: 19 additions & 3 deletions libs/icon-factory/src/lib/icon-factory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
* Every icon used in the application must be individually imported
* This ensures that we do not include icons that are not used in the application in the final bundle
*/
import { SerializedStyles } from '@emotion/react';
import { logger } from '@jetstream/shared/client-logger';
import classNames from 'classnames';
import ActionIcon_Announcement from './icons/action/Announcement';
import BrandIcon_Jetstream from './icons/brand/Jetstream';
import BrandIcon_JetstreamInverse from './icons/brand/JetstreamInverse';
import CustomIcon_Heart from './icons/custom/Heart';
Expand Down Expand Up @@ -141,14 +143,16 @@ import UtilityIcon_Warning from './icons/utility/Warning';

export type IconType = 'action' | 'custom' | 'doctype' | 'standard' | 'utility' | 'brand';

export type IconName = StandardIcon | CustomIcon | UtilityIcon | DoctypeIcon | BrandIcon;
export type IconName = ActionIcon | StandardIcon | CustomIcon | UtilityIcon | DoctypeIcon | BrandIcon;

export type ActionIconObj = typeof actionIcons;
export type StandardIconObj = typeof standardIcons;
export type CustomIconObj = typeof customIcons;
export type DoctypeIconObj = typeof doctypeIcons;
export type UtilityIconObj = typeof utilityIcons;
export type BrandIconObj = typeof brandIcons;

export type ActionIcon = keyof ActionIconObj;
export type StandardIcon = keyof StandardIconObj;
export type CustomIcon = keyof CustomIconObj;
export type DoctypeIcon = keyof DoctypeIconObj;
Expand All @@ -162,6 +166,10 @@ export interface IconObj {
description?: string;
}

const actionIcons = {
announcement: ActionIcon_Announcement,
} as const;

const standardIcons = {
actions_and_buttons: StandardIcon_ActionsAndButtons,
activations: StandardIcon_Activations,
Expand Down Expand Up @@ -311,10 +319,16 @@ const brandIcons = {
jetstream_inverse: BrandIcon_JetstreamInverse,
} as const;

export function getIcon(type: IconType, icon: string, className?: string) {
export function getIcon(type: IconType, icon: string, className?: string, svgCss?: SerializedStyles) {
let found = false;
let IconOrFallback = UtilityIcon_Fallback;
switch (type) {
case 'action':
if (actionIcons[icon]) {
IconOrFallback = actionIcons[icon];
found = true;
}
break;
case 'standard':
if (standardIcons[icon]) {
IconOrFallback = standardIcons[icon];
Expand Down Expand Up @@ -351,11 +365,13 @@ export function getIcon(type: IconType, icon: string, className?: string) {
if (!found) {
logger.warn('[ICON NOT FOUND]', `icon ${type}-${icon} not found, providing fallback`);
}
return <IconOrFallback className={classNames(className || 'slds-icon')} />;
return <IconOrFallback className={classNames(className || 'slds-icon')} css={svgCss} />;
}

export function getIconTypes(type: Omit<IconType, 'action' | 'custom'>): IconName[] {
switch (type) {
case 'action':
return Object.keys(actionIcons) as ActionIcon[];
case 'doctype':
return Object.keys(doctypeIcons) as StandardIcon[];
case 'standard':
Expand Down
49 changes: 49 additions & 0 deletions libs/shared/ui-core/src/app/HeaderAnnouncementPopover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { css } from '@emotion/react';
import { Icon, Popover, PopoverRef } from '@jetstream/ui';
import { FunctionComponent, ReactNode, useEffect, useRef } from 'react';

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface HeaderAnnouncementPopoverProps {
children: ReactNode;
}

export const HeaderAnnouncementPopover: FunctionComponent<HeaderAnnouncementPopoverProps> = ({ children }) => {
const isMounted = useRef(true);
const popoverRef = useRef<PopoverRef>(null);

useEffect(() => {
isMounted.current = true;
return () => {
isMounted.current = false;
};
}, []);

return (
<Popover
ref={popoverRef}
placement="bottom-end"
header={
<header className="slds-popover__header">
<h2 className="slds-text-heading_small" title="Get Help">
Announcement
</h2>
</header>
}
content={children}
buttonProps={{
className:
'slds-button slds-button_icon slds-button_icon slds-button_icon-warning slds-button_icon-container slds-button_icon-small cursor-pointer',
}}
>
<Icon
type="action"
icon="announcement"
className="slds-button__icon slds-icon slds-icon-text-warning"
svgCss={css`
color: #fe9339;
`}
omitContainer
/>
</Popover>
);
};
38 changes: 34 additions & 4 deletions libs/shared/ui-core/src/app/HeaderNavbar.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { DropDownItem, Maybe, UserProfileUi } from '@jetstream/types';
import { Header, Navbar, NavbarItem, NavbarMenuItems } from '@jetstream/ui';
import { FeedbackLink, Header, Navbar, NavbarItem, NavbarMenuItems } from '@jetstream/ui';
import { Fragment, FunctionComponent, useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Link, useNavigate } from 'react-router-dom';
import { useRecoilState, useRecoilValue } from 'recoil';
import Jobs from '../jobs/Jobs';
import OrgsDropdown from '../orgs/OrgsDropdown';
import { SelectedOrgReadOnly } from '../orgs/SelectedOrgReadOnly';
import { RecordSearchPopover } from '../record/RecordSearchPopover';
import { applicationCookieState, selectUserPreferenceState } from '../state-management/app-state';
import { HeaderAnnouncementPopover } from './HeaderAnnouncementPopover';
import HeaderDonatePopover from './HeaderDonatePopover';
import HeaderHelpPopover from './HeaderHelpPopover';
import NotificationsRequestModal from './NotificationsRequestModal';
Expand Down Expand Up @@ -78,8 +79,37 @@ export const HeaderNavbar: FunctionComponent<HeaderNavbarProps> = ({ userProfile
const rightHandMenuItems = useMemo(() => {
return isChromeExtension
? [<RecordSearchPopover />, <Jobs />, <HeaderHelpPopover />]
: [<RecordSearchPopover />, <Jobs />, <HeaderHelpPopover />, <HeaderDonatePopover />];
}, [isChromeExtension]);
: [
<HeaderAnnouncementPopover>
<p className="">We are working on upgrades to our authentication and user management systems in the coming weeks.</p>
<p className="slds-text-title_caps slds-m-top_x-small">Upcoming Features:</p>
<ul className="slds-list_dotted slds-m-vertical_x-small">
<li>Multi-factor authentication</li>
<li>Visibility to all active sessions</li>
</ul>
<p className="slds-text-title_caps">Important information:</p>
<ul className="slds-list_dotted slds-m-vertical_x-small">
<li>All users will be signed out and need to sign back in</li>
<li>Some users may require a password reset to log back in</li>
</ul>
<hr className="slds-m-vertical_small" />
Stay tuned for a timeline. If you have any questions <FeedbackLink type="EMAIL" label="Send us an email" />.
{!!userProfile && !userProfile.email_verified && (
<>
<hr className="slds-m-vertical_small" />
<p className="slds-text-color_error">
Your email address is not verified, make sure <Link to="/settings">verify your email address</Link> or{' '}
<Link to="/settings">link a social identity</Link> to make sure you can continue to login.
</p>
</>
)}
</HeaderAnnouncementPopover>,
<RecordSearchPopover />,
<Jobs />,
<HeaderHelpPopover />,
<HeaderDonatePopover />,
];
}, [isChromeExtension, userProfile]);

return (
<Fragment>
Expand Down
1 change: 1 addition & 0 deletions libs/shared/ui-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './app/ConfirmPageChange';
export * from './app/DownloadFileStream';
export * from './app/EmailSupport';
export * from './app/ErrorBoundaryFallback';
export * from './app/HeaderAnnouncementPopover';
export * from './app/HeaderDonatePopover';
export * from './app/HeaderHelpPopover';
export * from './app/HeaderNavbar';
Expand Down
2 changes: 1 addition & 1 deletion libs/ui/src/lib/popover/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface PopoverProps {
bodyClassName?: string;
bodyStyle?: SerializedStyles;
placement?: Placement;
content: JSX.Element;
content: ReactNode;
header?: JSX.Element;
footer?: JSX.Element;
panelStyle?: CSSProperties;
Expand Down
21 changes: 17 additions & 4 deletions libs/ui/src/lib/widgets/Icon.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { IconName, IconType, getIcon } from '@jetstream/icon-factory';
import { SerializedStyles } from '@emotion/react';
import { getIcon, IconName, IconType } from '@jetstream/icon-factory';
import classNames from 'classnames';

export interface IconProps {
containerClassname?: string;
className?: string;
omitContainer?: boolean;
containerCss?: SerializedStyles;
svgCss?: SerializedStyles;
title?: string;
type: IconType;
icon: IconName;
Expand All @@ -14,15 +17,25 @@ export interface IconProps {
/**
* https://www.lightningdesignsystem.com/components/icons/
*/
export const Icon = ({ containerClassname, className, title, omitContainer = false, type, icon, description }: IconProps) => {
export const Icon = ({
containerClassname,
className,
title,
omitContainer = false,
type,
icon,
description,
containerCss,
svgCss,
}: IconProps) => {
containerClassname = containerClassname || '';
className = className || '';
const svg = getIcon(type, icon, className);
const svg = getIcon(type, icon, className, svgCss);
if (omitContainer) {
return svg;
} else {
return (
<span className={classNames('slds-icon_container', containerClassname)} title={title || description}>
<span className={classNames('slds-icon_container', containerClassname)} css={containerCss} title={title || description}>
{svg}
{(description || title) && <span className="slds-assistive-text">{description || title}</span>}
</span>
Expand Down

0 comments on commit 50bbd13

Please sign in to comment.