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

In-app messaging for product updates #1364

Merged
merged 22 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
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
4 changes: 2 additions & 2 deletions packages/components/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@labkey/components",
"version": "2.396.3",
"version": "2.397.0-fb-appReleaseMsg.6",
"description": "Components, models, actions, and utility functions for LabKey applications and pages",
"sideEffects": false,
"files": [
Expand Down
6 changes: 6 additions & 0 deletions packages/components/releaseNotes/components.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# @labkey/components
Components, models, actions, and utility functions for LabKey applications and pages.

### version 2.X
*Released*: X December 2023
- In-app messaging for product updates
- Add `ReleaseNote` component
- Updated Help icon to be a menu

### version 2.396.3
*Released*: 12 December 2023
- Issue 49199: Field Editor name input fix for jumping cursor to end of input
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ import {
WebDavFile,
} from './public/files/WebDav';
import { FileTree } from './internal/components/files/FileTree';
import { ReleaseNote } from './internal/components/notifications/ReleaseNote';
import { Notifications } from './internal/components/notifications/Notifications';
import { getPipelineActivityData, markAllNotificationsAsRead } from './internal/components/notifications/actions';
import {
Expand Down Expand Up @@ -1401,6 +1402,7 @@ export {
PIPELINE_JOB_NOTIFICATION_EVENT_ERROR,
SHARED_CONTAINER_PATH,
NotificationItemModel,
ReleaseNote,
Notifications,
ServerNotificationModel,
ServerActivityData,
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/internal/app/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export const BIOLOGICS_APP_PROPERTIES: AppProperties = {
moduleName: 'biologics',
searchPlaceholder: SEARCH_PLACEHOLDER,
dataClassUrlPart: REGISTRY_KEY,
releaseNoteLink: 'bioReleaseNotes',
};

export const SAMPLE_MANAGER_APP_PROPERTIES: AppProperties = {
Expand All @@ -154,6 +155,7 @@ export const SAMPLE_MANAGER_APP_PROPERTIES: AppProperties = {
moduleName: 'sampleManagement',
searchPlaceholder: SAMPLE_MANAGER_SEARCH_PLACEHOLDER,
dataClassUrlPart: SOURCES_KEY,
releaseNoteLink: 'releaseNotes',
};

export const FREEZER_MANAGER_APP_PROPERTIES: AppProperties = {
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/internal/app/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface AppProperties {
name: string;
productId: string;
searchPlaceholder?: string;
releaseNoteLink?: string;
}

// Note: this should stay in sync with the eln/src/ReferencingNotebooks.tsx props
Expand Down
5 changes: 5 additions & 0 deletions packages/components/src/internal/components/base/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@
* limitations under the License.
*/
import React from 'react';

import { InsufficientPermissionsAlert } from '../permissions/InsufficientPermissionsAlert';

import { ReleaseNote } from '../notifications/ReleaseNote';
import { isApp } from '../../app/utils';

import { PageHeader } from './PageHeader';

export interface PageProps {
Expand Down Expand Up @@ -92,6 +96,7 @@ export class Page extends React.Component<PageProps, any> {

return (
<div className="app-page">
{isApp() && <ReleaseNote />}
{!hasHeader && <PageHeader showNotifications={showNotifications} />}
{children}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,11 @@ import { useServerContext } from '../base/ServerContext';
import { AppProperties } from '../../app/models';
import {
getCurrentAppProperties,
isProductProjectsEnabled,
isProjectContainer,
isAppHomeFolder,
} from '../../app/utils';

import { Alert } from '../base/Alert';

import { isLoading, LoadingState } from '../../../public/LoadingState';
import { naturalSortByProperty } from '../../../public/sort';
import { resolveErrorMessage } from '../../util/messaging';
import { AppContext, useAppContext } from '../../AppContext';
import { Container } from '../base/models/Container';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import { User } from '../base/models/User';
import { UserMenuGroupImpl } from './UserMenuGroup';
import { MenuSectionModel } from './model';

beforeAll(() => {
beforeEach(() => {
LABKEY.devMode = false;
LABKEY.moduleContext = {};
});

describe('UserMenuGroup', () => {
Expand Down Expand Up @@ -97,25 +98,40 @@ describe('UserMenuGroup', () => {
sectionKey: 'user',
});

function verify(wrapper: ReactWrapper, userOptions?: string[], adminOptions?: string[], help?: boolean) {
function verifyMenuOptions(menu: any, options: string[]) {
const menuOptions = menu.find(MenuItem);
expect(menuOptions).toHaveLength(options?.length);
expect(menuOptions.at(0).text()).toEqual(options[0]);
for (let i = 0; i < options.length; i++) {
expect(menuOptions.at(i).text()).toEqual(options[i]);
}
}

function verify(wrapper: ReactWrapper, userOptions?: string[], adminOptions?: string[], helpOptions?: string[]) {
const userMenu = wrapper.find(Dropdown);
const userMenuOptions = userMenu.at(0).find(MenuItem);
expect(userMenuOptions).toHaveLength(userOptions?.length);
for (let i = 0; i < userOptions.length; i++) {
expect(userMenuOptions.at(i).text()).toEqual(userOptions[i]);
}

const dropdowns = wrapper.find(DropdownButton);

let dropdownCount = 0,
helpMenu,
adminMenu;
if (adminOptions?.length > 0) {
const adminMenu = wrapper.find(DropdownButton);
expect(adminMenu).toHaveLength(1);
const adminMenuOptions = adminMenu.find(MenuItem);
expect(adminMenuOptions).toHaveLength(adminOptions?.length);
for (let i = 0; i < adminOptions.length; i++) {
expect(adminMenuOptions.at(i).text()).toEqual(adminOptions[i]);
}
adminMenu = dropdowns.at(dropdownCount);
verifyMenuOptions(adminMenu, adminOptions);
dropdownCount += 1;
}
if (helpOptions?.length > 0) {
helpMenu = dropdowns.at(dropdownCount);
verifyMenuOptions(helpMenu, helpOptions);
dropdownCount += 1;
}

expect(wrapper.find('#nav-help-button').length).toEqual(help ? 1 : 0);
expect(dropdowns).toHaveLength(dropdownCount);
}

test('not initialized', () => {
Expand All @@ -130,7 +146,7 @@ describe('UserMenuGroup', () => {
});

const tree = mount(<UserMenuGroupImpl model={section} user={user} />);
verify(tree, ['', 'Sign In'], null, true);
verify(tree, ['', 'Sign In'], null, ['Help']);
});

test('no help icon', () => {
Expand All @@ -139,7 +155,7 @@ describe('UserMenuGroup', () => {
});

const tree = mount(<UserMenuGroupImpl model={noHelpSection} user={user} />);
verify(tree, ['Documentation', '', 'Sign In'], null, false);
verify(tree, ['Documentation', '', 'Sign In'], null, null);
});

test('with admin items', () => {
Expand All @@ -149,7 +165,7 @@ describe('UserMenuGroup', () => {

const tree = mount(<UserMenuGroupImpl model={withAdmins} user={user} />);

verify(tree, ['Profile', '', /* divider*/ 'Sign Out'], ['Application Settings'], true);
verify(tree, ['Profile', '', /* divider*/ 'Sign Out'], ['Application Settings'], ['Help']);
});

test('user logged in, but not in dev mode', () => {
Expand All @@ -158,7 +174,7 @@ describe('UserMenuGroup', () => {
});

const tree = mount(<UserMenuGroupImpl model={section} user={user} />);
verify(tree, ['Profile', '', /* divider*/ 'Sign Out'], null, true);
verify(tree, ['Profile', '', /* divider*/ 'Sign Out'], null, ['Help']);
});

test('user logged in dev mode', () => {
Expand All @@ -168,7 +184,7 @@ describe('UserMenuGroup', () => {
LABKEY.devMode = true;

const tree = mount(<UserMenuGroupImpl model={section} user={user} />);
verify(tree, ['Profile', '', /* divider*/ 'Sign Out'], ['Dev Tools', 'Enable Redux Tools'], true);
verify(tree, ['Profile', '', /* divider*/ 'Sign Out'], ['Dev Tools', 'Enable Redux Tools'], ['Help']);
});

test('user logged in extra items', () => {
Expand All @@ -184,7 +200,7 @@ describe('UserMenuGroup', () => {
);
const tree = mount(<UserMenuGroupImpl model={section} user={user} extraUserItems={extraUserItems} />);

verify(tree, ['Profile', 'Extra One', 'Extra Two', '', /* divider*/ 'Sign Out'], null, true);
verify(tree, ['Profile', 'Extra One', 'Extra Two', '', /* divider*/ 'Sign Out'], null, ['Help']);
});

test('user logged in extra dev mode items', () => {
Expand Down Expand Up @@ -219,7 +235,39 @@ describe('UserMenuGroup', () => {
tree,
['Profile', 'Extra One', 'Extra Two', '', /* divider*/ 'Sign Out'],
['Dev Tools', 'Enable Redux Tools', 'Extra Dev One', 'Extra Dev Two'],
true
['Help']
);
});

test('with release note, with help', () => {
LABKEY.moduleContext = {
samplemanagement: {
productId: 'SampleManager',
},
};

const user = new User({
isSignedIn: true,
});

const tree = mount(<UserMenuGroupImpl model={withAdmins} user={user} />);

verify(tree, ['Profile', '', /* divider*/ 'Sign Out'], ['Application Settings'], ['Help', 'Release Notes']);
});

test('with release note, without help', () => {
LABKEY.moduleContext = {
samplemanagement: {
productId: 'SampleManager',
},
};

const user = new User({
isSignedIn: false,
});

const tree = mount(<UserMenuGroupImpl model={noHelpSection} user={user} />);

verify(tree, ['Documentation', '', 'Sign In'], null, ['Release Notes']);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ import { User } from '../base/models/User';
import { devToolsActive, toggleDevTools } from '../../util/utils';

import { useServerContext } from '../base/ServerContext';
import { getCurrentAppProperties, getPrimaryAppProperties } from '../../app/utils';
import { biologicsIsPrimaryApp, getCurrentAppProperties, getPrimaryAppProperties } from '../../app/utils';
import { AppProperties } from '../../app/models';

import { AppContext, useAppContext } from '../../AppContext';

import { getHelpLink } from '../../util/helpLinks';

import { signIn, signOut } from './actions';
import { MenuSectionModel } from './model';

Expand All @@ -48,6 +50,14 @@ interface ImplProps {
// exported for jest testing
export const UserMenuGroupImpl: FC<UserMenuProps & ImplProps> = props => {
const { model, extraDevItems, extraUserItems, onSignIn, onSignOut, user, signOutUrl } = props;
const releaseNoteLink = getPrimaryAppProperties()?.releaseNoteLink;
const releaseNoteHref = releaseNoteLink
? getHelpLink(
getPrimaryAppProperties()?.releaseNoteLink,
null,
biologicsIsPrimaryApp() /* needed for FM in Biologics*/
)
: undefined;

const { helpHref, userMenuItems, adminMenuItems } = useMemo(() => {
let helpHref;
Expand Down Expand Up @@ -136,13 +146,27 @@ export const UserMenuGroupImpl: FC<UserMenuProps & ImplProps> = props => {
</DropdownButton>
</div>
)}
{helpHref && (
<div className="navbar-item pull-right">
<div className="btn navbar-icon-button-right" id="nav-help-button">
<a href={helpHref} target="_blank" rel="noopener noreferrer">
<i className="fa fa-question-circle navbar-header-icon" />
</a>
</div>
{(!!helpHref || !!releaseNoteHref) && (
<div className="navbar-item pull-right navbar-item-product-navigation">
<DropdownButton
id="help-menu-button"
className="navbar-icon-button-right"
title={<i className="fa fa-question-circle navbar-header-icon" />}
noCaret
pullRight
>
<div className="navbar-icon-connector" />
{helpHref && (
<MenuItem key="help" href={helpHref} target="_blank" rel="noopener noreferrer">
Help
</MenuItem>
)}
{releaseNoteHref && (
<MenuItem key="release" href={releaseNoteHref} target="_blank" rel="noopener noreferrer">
Release Notes
</MenuItem>
)}
</DropdownButton>
</div>
)}
</>
Expand Down
Loading