Skip to content
This repository was archived by the owner on Mar 15, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all 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/common/src/components/Console/Console.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const logs = [
"This is a test of an ERROR message. Also, this error message happens to be very very long. Super long. It's only purpose is to be super long. So long that we can test that the log container properly resizes itself and shows all of this super important, meaningful text that will help us understand if this log will be readable by the user.",
severity: ConsoleLogSeverities.Error,
},
];
].map((log, i) => ({ ...log, id: i.toString() }));

const lotsOfLogs = [
{
Expand Down Expand Up @@ -695,7 +695,7 @@ const lotsOfLogs = [
message: 'This is a test of an ERROR message.',
severity: ConsoleLogSeverities.Error,
},
];
].map((log, i) => ({ ...log, id: i.toString() }));

const props: IProps = {
logs,
Expand Down
75 changes: 28 additions & 47 deletions packages/common/src/components/Console/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { withTheme } from 'styled-components';

import { Icon } from 'office-ui-fabric-react/lib/Icon';
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
import { List, ScrollToMode, IListProps } from 'office-ui-fabric-react/lib/List';
import { FocusZone } from 'office-ui-fabric-react/lib/FocusZone';

import {
Wrapper,
Expand All @@ -18,6 +16,8 @@ import {
} from './styles';
import HeaderFooterLayout from '../HeaderFooterLayout';

const MAX_LOGS_SHOWN = 100;

export enum ConsoleLogSeverities {
Info = 'info',
Log = 'log',
Expand All @@ -41,17 +41,13 @@ interface IState {
}

class Console extends React.Component<IPrivateProps, IState> {
private list: List;
private lastLog = React.createRef<HTMLDivElement>();
state: IState = { shouldScrollToBottom: true, filterQuery: '' };

static defaultProps = {
style: {},
};

constructor(props: IPrivateProps) {
super(props);
this.state = { shouldScrollToBottom: true, filterQuery: '' };
}

componentDidMount() {
this.scrollToBottom();
}
Expand All @@ -69,17 +65,18 @@ class Console extends React.Component<IPrivateProps, IState> {
});

scrollToBottom() {
if (this.state.shouldScrollToBottom && this.list) {
this.list.scrollToIndex(this.props.logs.length);
if (this.state.shouldScrollToBottom && this.lastLog.current) {
this.lastLog.current.scrollIntoView();
}
}

render() {
const { theme, logs, clearLogs, style } = this.props;

const items = logs
.slice(-1 * MAX_LOGS_SHOWN) // get the last X logs
.filter(log => log.message.toLowerCase().includes(this.state.filterQuery))
.map(({ severity, message }, i) => {
.map(({ id, severity, message }) => {
const { backgroundColor, color, icon } = {
[ConsoleLogSeverities.Log]: {
backgroundColor: theme.white,
Expand All @@ -104,7 +101,7 @@ class Console extends React.Component<IPrivateProps, IState> {
}[severity];

return {
key: `${severity}-${i}`,
key: `${severity}-${id}}`,
backgroundColor,
color,
icon,
Expand Down Expand Up @@ -153,46 +150,30 @@ class Console extends React.Component<IPrivateProps, IState> {
}
>
<LogsArea>
<div>
<List
ref={this.resolveList}
items={items}
onRenderCell={this.onRenderCell}
/>
</div>
{items.map(({ backgroundColor, color, key, icon, message }) => (
<Log key={key} style={{ backgroundColor, color }}>
{icon ? (
<Icon
className="ms-font-m"
iconName={icon.name}
style={{
fontSize: '1.2rem',
color: icon.color,
lineHeight: '1.2rem',
}}
/>
) : (
<div style={{ width: '1.2rem', height: '1.2rem' }} />
)}
<LogText>{message}</LogText>
</Log>
))}
<div ref={this.lastLog} />
</LogsArea>
</HeaderFooterLayout>
</Wrapper>
);
}

private resolveList = (list: List): void => {
this.list = list;
};

private onRenderCell = (
{ key, backgroundColor, color, icon, message }: any,
index: number,
): JSX.Element => {
return (
<Log key={key} style={{ backgroundColor, color }}>
{icon ? (
<Icon
className="ms-font-m"
iconName={icon.name}
style={{
fontSize: '1.2rem',
color: icon.color,
lineHeight: '1.2rem',
}}
/>
) : (
<div style={{ width: '1.2rem', height: '1.2rem' }} />
)}
<LogText>{message}</LogText>
</Log>
);
};
}

export default withTheme(Console);
1 change: 1 addition & 0 deletions packages/common/src/interfaces/logs.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
type ConsoleLogTypes = 'log' | 'info' | 'warn' | 'error';

interface ILogData {
id: string;
message: string;
severity: ConsoleLogTypes;
}
22 changes: 18 additions & 4 deletions packages/runner/src/components/App/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const RefreshBar = props => (
/>
);

let logCount = 0;

interface IState {
solution?: ISolution | null;
lastRendered: number | null;
Expand Down Expand Up @@ -89,16 +91,27 @@ export class App extends React.Component<{}, IState> {
});
};

addLog = (log: ILogData) =>
this.setState({ logs: [...this.state.logs, log], isConsoleOpen: true });
addLog = (log: { severity: ConsoleLogTypes; message: string }) => {
this.setState({
logs: [...this.state.logs, { id: logCount.toString(), ...log }],
isConsoleOpen: true,
});
logCount++;
};
clearLogs = () => this.setState({ logs: [] });

openConsole = () => this.setState({ isConsoleOpen: true });
closeConsole = () => this.setState({ isConsoleOpen: false });

onReceiveNewActiveSolution = (solution: ISolution | null) => {
if (solution !== null && this.state.solution) {
console.info(`Your snippet '${solution.name}' has been loaded.`);
if (solution !== null) {
if (!this.state.solution) {
console.info(`Your snippet "${solution.name}" has been loaded.`);
} else if (this.state.solution.id === solution.id) {
console.info(`Updating your snippet "${solution.name}".`);
} else {
console.info(`Switching to snippet "${solution.name}".`);
}
}
this.setState({ solution });
};
Expand All @@ -108,6 +121,7 @@ export class App extends React.Component<{}, IState> {
this.setState({
solution: { ...this.state.solution, dateLastModified: Date.now() },
});
console.info(`Your snippet '${this.state.solution.name}' has been reloaded.`);
}
};

Expand Down