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: Deep linking for the notification page #1667

Merged
merged 54 commits into from
Dec 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
729c2bf
deeplink for lg
lei9444 Nov 26, 2019
b956bd0
make lu link to dialog
lei9444 Nov 26, 2019
5728996
Merge branch 'master' into deeplink
lei9444 Nov 27, 2019
49e4940
make lu inline editor show error
lei9444 Nov 27, 2019
fd4fdb2
fix the lgeditor coderange error
lei9444 Nov 27, 2019
c58f800
fix code range type
lei9444 Nov 27, 2019
971376c
fix test
lei9444 Nov 27, 2019
f83a043
remove wait
lei9444 Nov 27, 2019
926ec36
edit e2e azure pipelines
lei9444 Nov 28, 2019
7252278
update e2e dir
lei9444 Nov 28, 2019
cedfece
fix some comments
lei9444 Nov 28, 2019
9ee13c1
Merge branch 'master' into deeplink
lei9444 Nov 29, 2019
27762f0
fix unit test
lei9444 Nov 29, 2019
678be71
Merge branch 'master' into deeplink
lei9444 Dec 2, 2019
216f243
add dialog check
lei9444 Dec 2, 2019
b86b979
add dialog error jump
lei9444 Dec 2, 2019
b0b72b3
fix lg templete type error
lei9444 Dec 2, 2019
4dadf1c
fix lint
lei9444 Dec 2, 2019
e291bcc
Merge branch 'master' into deeplink
lei9444 Dec 3, 2019
460b944
change the build order
lei9444 Dec 3, 2019
aba5ce6
fix conflict
lei9444 Dec 3, 2019
ae2ee21
fix some type
lei9444 Dec 3, 2019
b9b2ef1
disable start button if bot has error
lei9444 Dec 3, 2019
7136308
hide the emulater button
lei9444 Dec 3, 2019
4f4e4dc
fix e2e test and lg page crash
lei9444 Dec 3, 2019
4243ea5
add some e2e test and unit test
lei9444 Dec 3, 2019
2533c56
fix some comment
lei9444 Dec 4, 2019
f9f4d17
Merge branch 'master' into deeplink
lei9444 Dec 4, 2019
5174fae
move lgutil from shared to indexers
lei9444 Dec 4, 2019
afc3581
add e2e test
lei9444 Dec 4, 2019
6d90ee7
use indexer types
lei9444 Dec 4, 2019
aa90724
Merge branch 'master' into deeplink
lei9444 Dec 4, 2019
1d3d026
Merge branch 'master' into deeplink
lei9444 Dec 5, 2019
5a027ef
add schema to check expression field
lei9444 Dec 5, 2019
be2c128
make empty expression as waining
lei9444 Dec 5, 2019
9ebf451
fix lint and e2e test
lei9444 Dec 5, 2019
d1fbec1
fix test
lei9444 Dec 5, 2019
a264147
add some unit test
lei9444 Dec 5, 2019
7f8e233
fix a dialog nav bug
lei9444 Dec 5, 2019
940e37a
fix some ui comments
lei9444 Dec 6, 2019
940c85f
Merge branch 'master' into deeplink
lei9444 Dec 6, 2019
c65df3a
remove some code
lei9444 Dec 6, 2019
bac053f
fix unit test
lei9444 Dec 6, 2019
cd3c006
add empty check
lei9444 Dec 6, 2019
02c9f2f
make notifications single click
lei9444 Dec 6, 2019
73e51de
clean the tsconfig in shared
lei9444 Dec 6, 2019
0e4665a
fix list header
lei9444 Dec 6, 2019
cecc4c2
update the style
lei9444 Dec 6, 2019
f1b7319
update the e2e test
lei9444 Dec 6, 2019
cc9e72e
Merge branch 'master' into deeplink
lei9444 Dec 6, 2019
f583103
fix some conflict
lei9444 Dec 6, 2019
0b59aba
implement Pagination
lei9444 Dec 6, 2019
33a4386
fix pagination select from dropdown error
lei9444 Dec 6, 2019
7ad6124
fix expression function error
lei9444 Dec 7, 2019
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
62 changes: 57 additions & 5 deletions Composer/cypress/integration/NotificationPage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,69 @@ context('Notification Page', () => {

it('can show lg syntax error ', () => {
cy.visitPage('Bot Responses');
// left nav tree
cy.contains('TodoSample.Main');
cy.contains('All');

cy.get('.toggleEditMode button').click();
cy.get('.toggleEditMode button').as('switchButton');
cy.get('@switchButton').click();
cy.get('textarea').type('test lg syntax error');

cy.visitPage('Notifications');

cy.get('[data-testid="notifications-table-view"]').within(() => {
cy.findByText('common.lg').should('exist');
cy.findAllByText('common.lg')
.should('exist')
.first()
.click();
});

cy.findAllByText('Bot Responses').should('exist');
cy.get('@switchButton').should('be.disabled');
});

it('can show lu syntax error ', () => {
cy.visitPage('User Input');

cy.get('.dialogNavTree button[title="__TestTodoSample.Main"]').click({ multiple: true });

cy.get('.toggleEditMode button').click();
cy.get('textarea').type('test lu syntax error');

cy.visitPage('Notifications');

cy.get('[data-testid="notifications-table-view"]').within(() => {
cy.findAllByText('Main.lu')
.should('exist')
.first()
.click();
});

cy.findAllByText('__TestTodoSample.Main').should('exist');
});

it('can show dialog expression error ', () => {
cy.visitPage('Design Flow');

cy.findByTestId('ProjectTree').within(() => {
cy.findByText('Greeting').click();
});

cy.withinEditor('VisualEditor', () => {
cy.findByText('Greeting').should('exist');
});

cy.withinEditor('FormEditor', () => {
cy.findByText('Condition').should('exist');
cy.get('.ObjectItem input').type('()');
});

cy.visitPage('Notifications');

cy.get('[data-testid="notifications-table-view"]').within(() => {
cy.findAllByText('Main.dialog')
.should('exist')
.first()
.click();
});

cy.findAllByText('Greeting').should('exist');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@ import { fireEvent, render } from 'react-testing-library';
import { NotificationHeader } from '../../src/pages/notifications/NotificationHeader';

describe('<NotificationHeader/>', () => {
const items = ['test1', 'test2', 'test3'];
it('should render the NotificationHeader', () => {
const mockOnChange = jest.fn(() => null);
const { container } = render(<NotificationHeader items={items} onChange={mockOnChange} />);
const { container } = render(<NotificationHeader onChange={mockOnChange} />);

expect(container).toHaveTextContent('Notifications');
expect(container).toHaveTextContent('All');
const dropdown = container.querySelector('[data-testid="notifications-dropdown"]');
fireEvent.click(dropdown);
const test = document.querySelector('.ms-Dropdown-callout');
expect(test).toHaveTextContent('test1');
expect(test).toHaveTextContent('test2');
expect(test).toHaveTextContent('Error');
expect(test).toHaveTextContent('Warning');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,36 @@

import * as React from 'react';
import { render } from 'react-testing-library';
import formatMessage from 'format-message';

import { NotificationList } from '../../src/pages/notifications/NotificationList';

describe('<NotificationList/>', () => {
const items = [
lei9444 marked this conversation as resolved.
Show resolved Hide resolved
{ type: 'Error', location: 'test1', message: 'error1' },
{ type: 'Warning', location: 'test2', message: 'error2' },
{ type: 'Error', location: 'test3', message: 'error3' },
{
id: 'Main.dialog',
severity: formatMessage('Error'),
type: 'dialog',
location: formatMessage('test1'),
message: formatMessage('error1'),
diagnostic: '',
},
{
id: 'Main.lu',
severity: formatMessage('Warning'),
type: 'lu',
location: formatMessage('test2'),
message: formatMessage('error2'),
diagnostic: '',
},
{
id: 'common.lg',
severity: formatMessage('Error'),
type: 'lg',
location: formatMessage('test3'),
message: formatMessage('error3'),
diagnostic: '',
},
];
it('should render the NotificationList', () => {
const { container } = render(<NotificationList items={items} />);
Expand Down
4 changes: 2 additions & 2 deletions Composer/packages/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ const topLinks = (botLoaded: boolean) => {
// disabled: true, // will delete
// },
{
to: 'language-generation/',
to: '/language-generation',
iconName: 'Robot',
labelName: formatMessage('Bot Responses'),
exact: false,
disabled: !botLoaded,
},
{
to: 'language-understanding/',
to: '/language-understanding',
iconName: 'People',
labelName: formatMessage('User Input'),
exact: false,
Expand Down
2 changes: 1 addition & 1 deletion Composer/packages/client/src/ShellApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export const ShellApi: React.FC = () => {
});

const content = lgUtil.updateTemplate(file.content, templateName, template);
return lgUtil.checkLgContent(content);
return lgUtil.checkLgContent(content, id);
}

function copyLgTemplateHandler({ id, fromTemplateName, toTemplateName }, event) {
Expand Down
48 changes: 35 additions & 13 deletions Composer/packages/client/src/TestController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@
/** @jsx jsx */
import { jsx } from '@emotion/core';
import React, { useState, useRef, Fragment, useContext, useEffect, useCallback } from 'react';
import { ActionButton, PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { ActionButton, PrimaryButton, DefaultButton, IconButton } from 'office-ui-fabric-react/lib/Button';
import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner';
import { Callout } from 'office-ui-fabric-react/lib/Callout';
import { Stack } from 'office-ui-fabric-react/lib/Stack';
import formatMessage from 'format-message';
import { DialogInfo } from '@bfc/shared';
import { DiagnosticSeverity, Diagnostic } from '@bfc/indexers';

import settingsStorage from './utils/dialogSettingStorage';
import { StoreContext } from './store';
import { bot, botButton, calloutLabel, calloutDescription, calloutContainer } from './styles';
import { bot, botButton, calloutLabel, calloutDescription, calloutContainer, errorButton, errorCount } from './styles';
import { BotStatus, LuisConfig, Text } from './constants';
import { PublishLuisDialog } from './publishDialog';
import { OpenAlertModal, DialogStyle } from './components/Modal';
import { isAbsHosted } from './utils/envUtil';
import { getReferredFiles } from './utils/luUtil';
import useNotifications from './pages/notifications/useNotifications';
import { navigateTo } from './utils';

const openInEmulator = (url, authSettings: { MicrosoftAppId: string; MicrosoftAppPassword: string }) => {
// this creates a temporary hidden iframe to fire off the bfemulator protocol
Expand Down Expand Up @@ -46,11 +48,13 @@ export const TestController: React.FC = () => {
const [error, setError] = useState({ title: '', message: '' });
const [luisPublishSucceed, setLuisPublishSucceed] = useState(true);
const botActionRef = useRef(null);
const notifications = useNotifications();
const { botEndpoint, botName, botStatus, dialogs, toStartBot, luFiles, settings } = state;
const { connectBot, reloadBot, onboardingAddCoachMarkRef, publishLuis, startBot } = actions;
const connected = botStatus === BotStatus.connected;

const addRef = useCallback(startBot => onboardingAddCoachMarkRef({ startBot }), []);
const errorLength = notifications.filter(n => n.severity === 'Error').length;
const showError = errorLength > 0;

useEffect(() => {
toStartBot && handleClick();
Expand All @@ -69,16 +73,17 @@ export const TestController: React.FC = () => {
}

async function handleClick() {
const dialogErrors = dialogs.reduce<DialogInfo[]>((result, dialog) => {
if (dialog.diagnostics.length !== 0) {
return result.concat([dialog]);
const diagnostics = dialogs.reduce<Diagnostic[]>((result, dialog) => {
const errors = dialog.diagnostics.filter(n => n.severity === DiagnosticSeverity.Error);
if (errors.length !== 0) {
return result.concat(errors);
}
return result;
}, []);
if (dialogErrors.length !== 0) {
if (diagnostics.length !== 0) {
const title = `StaticValidationError`;
const subTitle = dialogErrors.reduce((msg, dialog) => {
msg += `\n In ${dialog.id}.dialog: \n ${dialog.diagnostics.join('\n')} \n`;
const subTitle = diagnostics.reduce((msg, diagnostic) => {
msg += `${diagnostic.message} \n`;
return msg;
}, '');

Expand Down Expand Up @@ -141,10 +146,14 @@ export const TestController: React.FC = () => {
}
}

function handleErrorButtonClick() {
navigateTo('/notifications/');
}

return (
<Fragment>
<div css={bot} ref={botActionRef}>
{connected && fetchState === STATE.SUCCESS && (
{connected && !showError && fetchState === STATE.SUCCESS && (
<ActionButton
iconProps={{
iconName: 'OpenInNewTab',
Expand All @@ -171,14 +180,27 @@ export const TestController: React.FC = () => {
labelPosition="left"
/>
)}
<span ref={addRef}>
<div ref={addRef}>
{showError && (
<Fragment>
<span css={errorCount}>{errorLength}</span>
<IconButton
iconProps={{ iconName: 'ErrorBadge' }}
css={errorButton}
title="Error"
ariaLabel="Error"
onClick={handleErrorButtonClick}
/>
</Fragment>
)}
<PrimaryButton
css={botButton}
text={connected ? formatMessage('Restart Bot') : formatMessage('Start Bot')}
onClick={handleClick}
id={'publishAndConnect'}
disabled={showError}
/>
</span>
</div>
<Callout
role="alertdialog"
ariaLabelledBy="callout-label-id"
Expand Down
65 changes: 65 additions & 0 deletions Composer/packages/client/src/components/Pagination/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

/** @jsx jsx */
import { jsx } from '@emotion/core';
import { DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import { useState, useEffect } from 'react';

import { container, dropdownStyles, text } from './style';

export interface IPaginationProps {
pageCount: number;
onChange: (page: number) => void;
}

const createDropdownOption = (pageCount: number) => {
const options: IDropdownOption[] = [];
for (let i = 1; i <= pageCount; i++) {
options.push({ key: 'page' + i, text: '' + i });
}
return options;
};

export const Pagination: React.FC<IPaginationProps> = props => {
const [index, setIndex] = useState(0);
const { pageCount, onChange } = props;

useEffect(() => {
setIndex(0);
}, [pageCount]);

const handlePageSelected = (event: React.FormEvent<HTMLDivElement>, item?: IDropdownOption, index?: number) => {
setIndex(index || 0);
onChange(index ? index + 1 : 1);
};

const hanglePreviousClick = () => {
const current = index - 1;
setIndex(current);
onChange(current + 1);
};

const hangleNextClick = () => {
const current = index + 1;
setIndex(current);
onChange(current + 1);
};

return (
<div css={container}>
<DefaultButton text="< Previous" allowDisabledFocus onClick={hanglePreviousClick} disabled={index === 0} />
<span css={text}>Page</span>
<Dropdown
placeholder="Select options"
options={createDropdownOption(pageCount)}
styles={dropdownStyles}
selectedKey={`page${index + 1}`}
onChange={handlePageSelected}
/>
<span css={text}>of {pageCount}</span>
<DefaultButton text="Next >" allowDisabledFocus onClick={hangleNextClick} disabled={index === pageCount - 1} />
</div>
);
};
24 changes: 24 additions & 0 deletions Composer/packages/client/src/components/Pagination/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { css } from '@emotion/core';
import { IDropdownStyles } from 'office-ui-fabric-react/lib/Dropdown';

export const dropdownStyles: Partial<IDropdownStyles> = {
dropdown: { width: 80 },
};

export const container = css`
display: flex;
width: 400px;
height: 45px;
padding-top: 5px;
line-height: 30px;
background-color: transparent;
justify-content: space-around;
`;

export const text = css`
font-size: 12px;
cursor: default;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Stack } from 'office-ui-fabric-react/lib/Stack';
import { IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import { Dropdown } from 'office-ui-fabric-react/lib/Dropdown';
import get from 'lodash/get';
import { DialogInfo } from '@bfc/shared';
import { DialogInfo } from '@bfc/indexers';

import {
addNewTrigger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { SearchBox } from 'office-ui-fabric-react/lib/SearchBox';
import { IIconProps } from 'office-ui-fabric-react/lib/Icon';
import cloneDeep from 'lodash/cloneDeep';
import formatMessage from 'format-message';
import { DialogInfo, ITrigger } from '@bfc/shared';
import { DialogInfo, ITrigger } from '@bfc/indexers';

import { StoreContext } from '../../store';
import { createSelectedPath, getFriendlyName } from '../../utils';
Expand Down
Loading