chore: add storybook dep (#5082)
This commit is contained in:
parent
043ed975c1
commit
c757ba72a9
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -18,3 +18,4 @@ drivers/
|
|||
.gradle/
|
||||
nohup.out
|
||||
api.json
|
||||
.trace
|
||||
11
.storybook/main.js
Normal file
11
.storybook/main.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
module.exports = {
|
||||
"stories": [
|
||||
"../src/cli/traceViewer/web/ui/*.stories.tsx",
|
||||
],
|
||||
"addons": [
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
],
|
||||
"typescript": {
|
||||
}
|
||||
}
|
||||
16
.storybook/preview.js
Normal file
16
.storybook/preview.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { addDecorator } from '@storybook/react';
|
||||
import { GlobalStyles } from '../src/cli/traceViewer/web/styles';
|
||||
import { applyTheme } from '../src/cli/traceViewer/web/theme';
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
}
|
||||
|
||||
|
||||
addDecorator(storyFn => {
|
||||
applyTheme();
|
||||
return <div style={{backgroundColor: 'var(--background)'}}>
|
||||
<GlobalStyles />
|
||||
{storyFn()}
|
||||
</div>
|
||||
});
|
||||
7648
package-lock.json
generated
7648
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -26,7 +26,9 @@
|
|||
"test-types": "node utils/generate_types/ && npx -p typescript@3.7.5 tsc -p utils/generate_types/test/tsconfig.json && tsc -p ./test/",
|
||||
"roll-browser": "node utils/roll_browser.js",
|
||||
"check-deps": "node utils/check_deps.js",
|
||||
"build-android-driver": "./utils/build_android_driver.sh"
|
||||
"build-android-driver": "./utils/build_android_driver.sh",
|
||||
"storybook": "start-storybook -p 6006 -s public",
|
||||
"build-storybook": "build-storybook -s public"
|
||||
},
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
|
|
@ -84,6 +86,11 @@
|
|||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"socksv5": "0.0.6",
|
||||
"@storybook/addon-actions": "^6.1.14",
|
||||
"@storybook/addon-essentials": "^6.1.14",
|
||||
"@storybook/addon-links": "^6.1.14",
|
||||
"@storybook/node-logger": "^6.1.14",
|
||||
"@storybook/react": "^6.1.14",
|
||||
"style-loader": "^1.2.1",
|
||||
"ts-loader": "^8.0.3",
|
||||
"typescript": "^4.0.2",
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ async function codegen(options: Options, url: string | undefined, target: string
|
|||
const { context, browserName, launchOptions, contextOptions } = await launchContext(options, false);
|
||||
|
||||
if (process.env.PWTRACE)
|
||||
contextOptions.recordVideo = { dir: path.join(process.cwd(), '.trace') };
|
||||
(contextOptions as any)._traceDir = path.join(process.cwd(), '.trace');
|
||||
|
||||
const outputs: CodeGeneratorOutput[] = [TerminalOutput.create(process.stdout, languageGenerator.highlighterType())];
|
||||
if (outputFile)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ export type PageEntry = {
|
|||
export type ActionEntry = {
|
||||
actionId: string;
|
||||
action: trace.ActionTraceEvent;
|
||||
thumbnailUrl: string;
|
||||
resources: trace.NetworkResourceTraceEvent[];
|
||||
};
|
||||
|
||||
|
|
@ -108,9 +109,11 @@ export function readTraceFile(events: trace.TraceEvent[], traceModel: TraceModel
|
|||
}
|
||||
case 'action': {
|
||||
const pageEntry = pageEntries.get(event.pageId!)!;
|
||||
const actionId = event.contextId + '/' + event.pageId + '/' + pageEntry.actions.length;
|
||||
const action: ActionEntry = {
|
||||
actionId: event.contextId + '/' + event.pageId + '/' + pageEntry.actions.length,
|
||||
actionId,
|
||||
action: event,
|
||||
thumbnailUrl: `action-preview/${actionId}.png`,
|
||||
resources: pageEntry.resources,
|
||||
};
|
||||
pageEntry.resources = [];
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@
|
|||
*/
|
||||
|
||||
import { TraceModel, VideoMetaInfo, trace } from '../traceModel';
|
||||
import './common.css';
|
||||
import './third_party/vscode/codicon.css';
|
||||
import { Workbench } from './ui/workbench';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { applyTheme } from './theme';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
|
@ -30,27 +30,8 @@ declare global {
|
|||
}
|
||||
}
|
||||
|
||||
function platformName(): string {
|
||||
if (window.navigator.userAgent.includes('Linux'))
|
||||
return 'platform-linux';
|
||||
if (window.navigator.userAgent.includes('Windows'))
|
||||
return 'platform-windows';
|
||||
if (window.navigator.userAgent.includes('Mac'))
|
||||
return 'platform-mac';
|
||||
return 'platform-generic';
|
||||
}
|
||||
|
||||
(async () => {
|
||||
document!.defaultView!.addEventListener('focus', (event: any) => {
|
||||
if (event.target.document.nodeType === Node.DOCUMENT_NODE)
|
||||
document.body.classList.remove('inactive');
|
||||
}, false);
|
||||
document!.defaultView!.addEventListener('blur', event => {
|
||||
document.body.classList.add('inactive');
|
||||
}, false);
|
||||
|
||||
document.documentElement.classList.add(platformName());
|
||||
|
||||
applyTheme();
|
||||
const traceModel = await window.getTraceModel();
|
||||
ReactDOM.render(<Workbench traceModel={traceModel} />, document.querySelector('#root'));
|
||||
})();
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
|
||||
export const GlobalStyles = () => <style>{`
|
||||
:root {
|
||||
--light-background: #f3f2f1;
|
||||
--background: #edebe9;
|
||||
|
|
@ -51,6 +54,20 @@ html, body {
|
|||
overscroll-behavior-x: none;
|
||||
}
|
||||
|
||||
#root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--background);
|
||||
color: var(--color);
|
||||
font-size: 14px;
|
||||
font-family: SegoeUI-SemiBold-final,Segoe UI Semibold,SegoeUI-Regular-final,Segoe UI,"Segoe UI Web (West European)",Segoe,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,Tahoma,Helvetica,Arial,sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
|
|
@ -69,20 +86,6 @@ svg {
|
|||
fill: currentColor;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--background);
|
||||
color: var(--color);
|
||||
font-size: 14px;
|
||||
font-family: SegoeUI-SemiBold-final,Segoe UI Semibold,SegoeUI-Regular-final,Segoe UI,"Segoe UI Web (West European)",Segoe,-apple-system,BlinkMacSystemFont,Roboto,Helvetica Neue,Tahoma,Helvetica,Arial,sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
#root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.platform-windows {
|
||||
--monospace-font: Consolas, Inconsolata, "Courier New", monospace;
|
||||
}
|
||||
|
|
@ -121,3 +124,4 @@ body {
|
|||
::-webkit-scrollbar-corner {
|
||||
background-color: var(--background);
|
||||
}
|
||||
`}</style>;
|
||||
39
src/cli/traceViewer/web/theme.ts
Normal file
39
src/cli/traceViewer/web/theme.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function platformName(): string {
|
||||
if (window.navigator.userAgent.includes('Linux'))
|
||||
return 'platform-linux';
|
||||
if (window.navigator.userAgent.includes('Windows'))
|
||||
return 'platform-windows';
|
||||
if (window.navigator.userAgent.includes('Mac'))
|
||||
return 'platform-mac';
|
||||
return 'platform-generic';
|
||||
}
|
||||
|
||||
export function applyTheme() {
|
||||
if ((document as any).playwrightThemeInitialized)
|
||||
return;
|
||||
(document as any).playwrightThemeInitialized = true;
|
||||
document!.defaultView!.addEventListener('focus', (event: any) => {
|
||||
if (event.target.document.nodeType === Node.DOCUMENT_NODE)
|
||||
document.body.classList.remove('inactive');
|
||||
}, false);
|
||||
document!.defaultView!.addEventListener('blur', event => {
|
||||
document.body.classList.add('inactive');
|
||||
}, false);
|
||||
document.documentElement.classList.add(platformName());
|
||||
}
|
||||
20
src/cli/traceViewer/web/types.d.ts
vendored
Normal file
20
src/cli/traceViewer/web/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
declare module '*.png' {
|
||||
const value: any;
|
||||
export = value;
|
||||
}
|
||||
|
|
@ -19,9 +19,7 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: none;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
contain: size;
|
||||
padding: 0 var(--layout-gap);
|
||||
}
|
||||
|
||||
|
|
|
|||
64
src/cli/traceViewer/web/ui/actionList.stories.tsx
Normal file
64
src/cli/traceViewer/web/ui/actionList.stories.tsx
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { Story, Meta } from '@storybook/react/types-6-0';
|
||||
import { ActionList, ActionListProps } from './actionList';
|
||||
import gotoThumbnailUrl from './assets/action-thumbnail-goto.png';
|
||||
import clickThumbnailUrl from './assets/action-thumbnail-click.png';
|
||||
|
||||
export default {
|
||||
title: 'TraceViewer/ActionList',
|
||||
component: ActionList,
|
||||
backgrounds: {
|
||||
default: '#edebe9',
|
||||
}
|
||||
} as Meta;
|
||||
|
||||
const Template: Story<ActionListProps> = args => <ActionList {...args} />;
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
Primary.args = {
|
||||
actions: [
|
||||
{
|
||||
actionId: 'id2',
|
||||
action: {
|
||||
timestamp: Date.now(),
|
||||
type: 'action',
|
||||
contextId: '<context>',
|
||||
action: 'goto',
|
||||
value: 'https://github.com/microsoft',
|
||||
startTime: Date.now(),
|
||||
endTime: Date.now(),
|
||||
},
|
||||
thumbnailUrl: gotoThumbnailUrl,
|
||||
resources: [],
|
||||
},
|
||||
{
|
||||
actionId: 'id',
|
||||
action: {
|
||||
timestamp: Date.now(),
|
||||
type: 'action',
|
||||
contextId: '<context>',
|
||||
action: 'click',
|
||||
selector: 'input[aria-label="Find a repository…"]',
|
||||
startTime: Date.now(),
|
||||
endTime: Date.now(),
|
||||
},
|
||||
thumbnailUrl: clickThumbnailUrl,
|
||||
resources: [],
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
@ -18,23 +18,31 @@ import { ActionEntry } from '../../traceModel';
|
|||
import './actionList.css';
|
||||
import * as React from 'react';
|
||||
|
||||
export const ActionList: React.FunctionComponent<{
|
||||
export interface ActionListProps {
|
||||
actions: ActionEntry[],
|
||||
selectedAction: ActionEntry | undefined,
|
||||
highlightedAction: ActionEntry | undefined,
|
||||
onSelected: (action: ActionEntry) => void,
|
||||
onHighlighted: (action: ActionEntry | undefined) => void,
|
||||
}> = ({ actions, selectedAction, highlightedAction, onSelected, onHighlighted }) => {
|
||||
}
|
||||
|
||||
export const ActionList: React.FC<ActionListProps> = ({
|
||||
actions = [],
|
||||
selectedAction = undefined,
|
||||
highlightedAction = undefined,
|
||||
onSelected = () => {},
|
||||
onHighlighted = () => {},
|
||||
}) => {
|
||||
const targetAction = highlightedAction || selectedAction;
|
||||
return <div className='action-list'>{actions.map(actionEntry => {
|
||||
const { action, actionId } = actionEntry;
|
||||
const { action, actionId, thumbnailUrl } = actionEntry;
|
||||
return <div
|
||||
className={'action-entry' + (actionEntry === targetAction ? ' selected' : '')}
|
||||
key={actionId}
|
||||
onClick={() => onSelected(actionEntry)}
|
||||
onMouseEnter={() => onHighlighted(actionEntry)}
|
||||
onMouseLeave={() => (highlightedAction === actionEntry) && onHighlighted(undefined)}
|
||||
>
|
||||
>
|
||||
<div className='action-header'>
|
||||
<div className={'action-error codicon codicon-issues'} hidden={!actionEntry.action.error} />
|
||||
<div className='action-title'>{action.action}</div>
|
||||
|
|
@ -42,7 +50,7 @@ export const ActionList: React.FunctionComponent<{
|
|||
{action.action === 'goto' && action.value && <div className='action-url' title={action.value}>{action.value}</div>}
|
||||
</div>
|
||||
<div className='action-thumbnail'>
|
||||
{action.snapshot ? <img src={`action-preview/${actionId}.png`} /> : 'No snapshot available'}
|
||||
<img src={thumbnailUrl} />
|
||||
</div>
|
||||
</div>;
|
||||
})}</div>;
|
||||
|
|
|
|||
BIN
src/cli/traceViewer/web/ui/assets/action-thumbnail-click.png
Normal file
BIN
src/cli/traceViewer/web/ui/assets/action-thumbnail-click.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
src/cli/traceViewer/web/ui/assets/action-thumbnail-goto.png
Normal file
BIN
src/cli/traceViewer/web/ui/assets/action-thumbnail-goto.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 159 KiB |
|
|
@ -21,6 +21,7 @@ import { Timeline } from './timeline';
|
|||
import './workbench.css';
|
||||
import * as React from 'react';
|
||||
import { ContextSelector } from './contextSelector';
|
||||
import { GlobalStyles } from '../styles';
|
||||
|
||||
export const Workbench: React.FunctionComponent<{
|
||||
traceModel: TraceModel,
|
||||
|
|
@ -39,6 +40,7 @@ export const Workbench: React.FunctionComponent<{
|
|||
const snapshotSize = context.created.viewportSize || { width: 1280, height: 720 };
|
||||
|
||||
return <div className='vbox workbench'>
|
||||
<GlobalStyles />
|
||||
<div className='hbox header'>
|
||||
<div className='logo'>🎭</div>
|
||||
<div className='product'>Playwright</div>
|
||||
|
|
@ -60,10 +62,10 @@ export const Workbench: React.FunctionComponent<{
|
|||
highlightedAction={highlightedAction}
|
||||
onSelected={action => setSelectedAction(action)}
|
||||
onHighlighted={action => setHighlightedAction(action)}
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
<div className='hbox'>
|
||||
<div style={{ display: 'flex', flex: 'none' }}>
|
||||
<div style={{ display: 'flex', flex: 'none', overflow: 'auto' }}>
|
||||
<ActionList
|
||||
actions={actions}
|
||||
selectedAction={selectedAction}
|
||||
|
|
|
|||
|
|
@ -235,14 +235,6 @@ declare module 'highlight.js' {
|
|||
export = hljs;
|
||||
}
|
||||
|
||||
declare module 'highlight.js/lib/core' {
|
||||
export = hljs;
|
||||
}
|
||||
|
||||
declare module 'highlight.js/lib/core.js' {
|
||||
export = hljs;
|
||||
}
|
||||
|
||||
declare module 'highlight.js/lib/languages/*' {
|
||||
export default function(hljs?: HLJSApi): LanguageDetail;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue