Compare commits

..

22 Commits

Author SHA1 Message Date
sunilyadav840
ccac4b8741 revert changes 2021-09-28 19:49:26 +05:30
sunilyadav840
0e841e34a6 Merge branch 'master' 2021-09-28 19:36:01 +05:30
vaidankarswapnil
6ca8e3c6f4 Fix execute Query 'Results' and 'Query status' section controls gets truncated at 200% resize mode of 'Query1' blade (#1105)
* Fix a11y querytab results section zoom section issue

* Update test smapshot

* Update test snapshot

* Resolved test case issue
2021-09-27 11:03:01 -07:00
vaidankarswapnil
f7e7240010 Fix keyboard focus does not retain on 'New Database' button after closing the 'New Database' blade via ESC key (#1109)
* Fix a11y new database button focus issue

* Update test snapshot and other issues
2021-09-23 08:13:18 -07:00
Sunil Kumar Yadav
acc095a482 fixed graph input query JAWS screen reader issue (#1108) 2021-09-23 08:11:46 -07:00
Hardikkumar Nai
288e6410f3 Choose Column: Aria Position set is not defined for check box present under choose column dialog box. (#1104) 2021-09-23 07:59:10 -07:00
Hardikkumar Nai
9ac3392271 Scale and Settings: 'Learn more' link is not adapting the high contrast mode after applying high contrast theme. (#1103) 2021-09-23 07:58:41 -07:00
Sunil Kumar Yadav
9a908dde9a fixed graph explorer visibility and graph expand keyboard accessibility issue (#1092)
* fixed graph explorer visibility issue

* fixed graph expand keyboard accessibility issue
2021-09-23 07:57:42 -07:00
siddjoshi-ms
ddd2e63fe7 Telemetry added for calculate cost function (#1018)
* Added telemetry for sql cost calculation
2021-09-22 09:49:45 -07:00
Hardikkumar Nai
34c59b4872 Scale and Settings: Text content of 'Info status message' and 'Warning' message is not visible properly at high contrast black mode. (#1090) 2021-09-22 07:40:18 -07:00
Jordi Bunster
7d28af4fc7 Make these required fields (#1101) 2021-09-21 15:50:44 -07:00
Sunil Kumar Yadav
50b99ceb42 fixed horizontal scrollbar issue on 400% resize mode (#1099) 2021-09-21 09:06:49 -07:00
Sunil Kumar Yadav
15a26d6500 fixed notification content visible issue on 400 resize mode (#1098) 2021-09-21 09:06:17 -07:00
Sunil Kumar Yadav
a8150af269 fixed incorrect notification console expand collaped screenreader issue (#1095) 2021-09-21 09:04:47 -07:00
Sunil Kumar Yadav
6a9a0156a3 fixed entity choose column role accessibility issue (#1088) 2021-09-21 09:02:04 -07:00
vaidankarswapnil
ead28f043f Fix after activating "Refresh tree" button, 'Querying database' message appears but screen reader does not provide any information about it (#1091)
* Fix a11y refresh tree querying database msg

* Update test snapshot issue
2021-09-21 09:00:28 -07:00
vaidankarswapnil
b05e5a2145 Fix delete database warning message is not announced by the screen reader after selecting 'Delete Database' menu item (#1074)
* Fix a11y delete database confirmation ississue

* Resolved lint issue - Removed Unnecessary semicolon

* Resolved compilation issue for extractFeature.ts and update test snapshot issue

Co-authored-by: Armando Trejo Oliver <ato9000@users.noreply.github.com>
2021-09-21 08:58:03 -07:00
Hardikkumar Nai
5e8aa491ba Load Graph: Name, role and state properties are not defined for 'full screen graph view' control present under 'Graph' tab section. (#1083) 2021-09-21 08:57:08 -07:00
Hardikkumar Nai
a730c08292 New SQL Query: Luminosity contrast ratio is 2.6:1 which is less than 3:1 for Close(X) icon button of Query 1 tab control (#1089) 2021-09-21 08:56:16 -07:00
Hardikkumar Nai
3dce5cd129 Tooltip is not provided for 'More' control present on the page. (#1093) 2021-09-21 08:54:44 -07:00
siddjoshi-ms
7c186c3ef2 Update GraphAPICompute.rp.ts (#1065) 2021-09-20 15:13:07 -07:00
sunilyadav840
b4cc0f8e52 fixed test cases errors 2021-05-05 17:59:54 +05:30
40 changed files with 783 additions and 644 deletions

View File

@@ -1,16 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
version="1.1"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"> xmlns:xlink="http://www.w3.org/1999/xlink">
<path <path
d="M14,2.691l-5.301,5.309l5.301,5.309l-0.691,0.691l-5.309,-5.301l-5.309,5.301l-0.691,-0.691l5.301,-5.309l-5.301,-5.309l0.691,-0.691l5.309,5.301l5.309,-5.301l0.691,0.691Z" d="M14,2.691l-5.301,5.309l5.301,5.309l-0.691,0.691l-5.309,-5.301l-5.309,5.301l-0.691,-0.691l5.301,-5.309l-5.301,-5.309l0.691,-0.691l5.309,5.301l5.309,-5.301l0.691,0.691Z"
transform="scale(0.5)" transform="scale(0.5)" fill="#000" stroke="#000">
fill="#000"
stroke="#CCC"
>
</path> </path>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 503 B

After

Width:  |  Height:  |  Size: 449 B

View File

@@ -2357,6 +2357,8 @@ a:link {
height: 100%; height: 100%;
flex-grow: 1; flex-grow: 1;
overflow: hidden; overflow: hidden;
min-height: 300px;
overflow-y: scroll;
} }
.tabs { .tabs {
@@ -2832,6 +2834,8 @@ a:link {
#explorerNotificationConsole { #explorerNotificationConsole {
z-index: 1000; z-index: 1000;
overflow-y: auto;
overflow-x: clip;
} }
.uniqueIndexesContainer { .uniqueIndexesContainer {

View File

@@ -8,6 +8,9 @@
.input-type-head-text-field { .input-type-head-text-field {
width: 100%; width: 100%;
} }
.input-query-form {
width: 100%;
}
textarea { textarea {
width: 100%; width: 100%;
line-height: 1; line-height: 1;

View File

@@ -160,18 +160,21 @@ export class InputTypeaheadComponent extends React.Component<
return ( return (
<div className="input-typeahead-container"> <div className="input-typeahead-container">
<Stack horizontal> <Stack horizontal>
<TextField <form aria-labelledby="input" className="input-query-form">
multiline={useTextarea} <TextField
rows={1} multiline={useTextarea}
defaultValue={defaultValue} rows={1}
ariaLabel="Input query" id="input"
placeholder={placeholder} defaultValue={defaultValue}
className="input-type-head-text-field" ariaLabel="Input query"
value={defaultValue} placeholder={placeholder}
onKeyDown={this.onSubmit} className="input-type-head-text-field"
onFocus={() => this.setState({ isSuggestionVisible: true })} value={defaultValue}
onChange={(_event, newValue?: string) => this.handleChange(newValue)} onKeyDown={this.onSubmit}
/> onFocus={() => this.setState({ isSuggestionVisible: true })}
onChange={(_event, newValue?: string) => this.handleChange(newValue)}
/>
</form>
{this.props.showCancelButton && ( {this.props.showCancelButton && (
<IconButton <IconButton
styles={iconButtonStyles} styles={iconButtonStyles}

View File

@@ -7,16 +7,22 @@ exports[`inputTypeahead renders <input /> 1`] = `
<Stack <Stack
horizontal={true} horizontal={true}
> >
<StyledTextFieldBase <form
ariaLabel="Input query" aria-labelledby="input"
className="input-type-head-text-field" className="input-query-form"
multiline={false} >
onChange={[Function]} <StyledTextFieldBase
onFocus={[Function]} ariaLabel="Input query"
onKeyDown={[Function]} className="input-type-head-text-field"
placeholder="placeholder" id="input"
rows={1} multiline={false}
/> onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
placeholder="placeholder"
rows={1}
/>
</form>
</Stack> </Stack>
</div> </div>
`; `;
@@ -28,16 +34,22 @@ exports[`inputTypeahead renders <textarea /> 1`] = `
<Stack <Stack
horizontal={true} horizontal={true}
> >
<StyledTextFieldBase <form
ariaLabel="Input query" aria-labelledby="input"
className="input-type-head-text-field" className="input-query-form"
multiline={true} >
onChange={[Function]} <StyledTextFieldBase
onFocus={[Function]} ariaLabel="Input query"
onKeyDown={[Function]} className="input-type-head-text-field"
placeholder="placeholder" id="input"
rows={1} multiline={true}
/> onChange={[Function]}
onFocus={[Function]}
onKeyDown={[Function]}
placeholder="placeholder"
rows={1}
/>
</form>
</Stack> </Stack>
</div> </div>
`; `;

View File

@@ -65,7 +65,7 @@ export interface PriceBreakdown {
currencySign: string; currencySign: string;
} }
export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14 } }; export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14, color: "windowtext" } };
export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = { export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = {
label: { label: {
@@ -272,7 +272,7 @@ export const manualToAutoscaleDisclaimerElement: JSX.Element = (
<Text styles={infoAndToolTipTextStyle} id="manualToAutoscaleDisclaimerElement"> <Text styles={infoAndToolTipTextStyle} id="manualToAutoscaleDisclaimerElement">
The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings
and storage of your resource. After autoscale has been enabled, you can change the max RU/s.{" "} and storage of your resource. After autoscale has been enabled, you can change the max RU/s.{" "}
<a href={Urls.autoscaleMigration}>Learn more</a> <Link href={Urls.autoscaleMigration}>Learn more</Link>
</Text> </Text>
); );

View File

@@ -88,16 +88,18 @@ export class IndexingPolicyComponent extends React.Component<
private async createIndexingPolicyEditor(): Promise<void> { private async createIndexingPolicyEditor(): Promise<void> {
const value: string = JSON.stringify(this.props.indexingPolicyContent, undefined, 4); const value: string = JSON.stringify(this.props.indexingPolicyContent, undefined, 4);
const monaco = await loadMonaco(); const monaco = await loadMonaco();
this.indexingPolicyEditor = monaco.editor.create(this.indexingPolicyDiv.current, { if (monaco.editor) {
value: value, this.indexingPolicyEditor = monaco.editor.create(this.indexingPolicyDiv.current, {
language: "json", value: value,
readOnly: isIndexTransforming(this.props.indexTransformationProgress), language: "json",
ariaLabel: "Indexing Policy", readOnly: isIndexTransforming(this.props.indexTransformationProgress),
}); ariaLabel: "Indexing Policy",
if (this.indexingPolicyEditor) { });
const indexingPolicyEditorModel = this.indexingPolicyEditor.getModel(); if (this.indexingPolicyEditor) {
indexingPolicyEditorModel.onDidChangeContent(this.onEditorContentChange.bind(this)); const indexingPolicyEditorModel = this.indexingPolicyEditor.getModel();
this.props.logIndexingPolicySuccessMessage(); indexingPolicyEditorModel.onDidChangeContent(this.onEditorContentChange.bind(this));
this.props.logIndexingPolicySuccessMessage();
}
} }
} }

View File

@@ -8,6 +8,7 @@ exports[`IndexingPolicyRefreshComponent renders 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }

View File

@@ -20,6 +20,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -39,6 +40,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -73,6 +75,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -80,11 +83,11 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
> >
The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings and storage of your resource. After autoscale has been enabled, you can change the max RU/s. The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings and storage of your resource. After autoscale has been enabled, you can change the max RU/s.
<a <StyledLinkBase
href="https://aka.ms/cosmos-autoscale-migration" href="https://aka.ms/cosmos-autoscale-migration"
> >
Learn more Learn more
</a> </StyledLinkBase>
</Text> </Text>
</StyledMessageBar> </StyledMessageBar>
<StyledChoiceGroup <StyledChoiceGroup
@@ -186,6 +189,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -460,6 +464,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }

View File

@@ -16,6 +16,7 @@ exports[`ScaleComponent renders with correct initial notification 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }

View File

@@ -136,6 +136,7 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -412,6 +413,7 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -952,6 +954,7 @@ exports[`SubSettingsComponent renders 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -1228,6 +1231,7 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }

View File

@@ -159,6 +159,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -166,16 +167,17 @@ exports[`SettingsUtils functions render 1`] = `
> >
The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings and storage of your resource. After autoscale has been enabled, you can change the max RU/s. The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings and storage of your resource. After autoscale has been enabled, you can change the max RU/s.
<a <StyledLinkBase
href="https://aka.ms/cosmos-autoscale-migration" href="https://aka.ms/cosmos-autoscale-migration"
> >
Learn more Learn more
</a> </StyledLinkBase>
</Text> </Text>
<Text <Text
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -195,6 +197,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -207,6 +210,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -219,6 +223,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -230,6 +235,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -249,6 +255,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -264,6 +271,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -278,6 +286,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -291,6 +300,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -302,6 +312,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -321,6 +332,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -363,6 +375,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -378,6 +391,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }
@@ -394,6 +408,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={ styles={
Object { Object {
"root": Object { "root": Object {
"color": "windowtext",
"fontSize": 14, "fontSize": 14,
}, },
} }

View File

@@ -1,7 +1,6 @@
@import "../../../../less/Common/Constants"; @import "../../../../less/Common/Constants";
.tabComponentContainer { .tabComponentContainer {
overflow: hidden;
height: 100%; height: 100%;
.flex-display(); .flex-display();
.flex-direction(); .flex-direction();

View File

@@ -243,6 +243,7 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
<div ref={this.contextMenuRef} onContextMenu={this.onRightClick} onKeyPress={this.onMoreButtonKeyPress}> <div ref={this.contextMenuRef} onContextMenu={this.onRightClick} onKeyPress={this.onMoreButtonKeyPress}>
<IconButton <IconButton
name="More" name="More"
title="More"
className="treeMenuEllipsis" className="treeMenuEllipsis"
ariaLabel={menuItemLabel} ariaLabel={menuItemLabel}
menuIconProps={{ menuIconProps={{

View File

@@ -211,6 +211,7 @@ exports[`TreeNodeComponent renders a simple node (sorted children, expanded) 1`]
}, },
} }
} }
title="More"
/> />
</div> </div>
</div> </div>
@@ -423,6 +424,7 @@ exports[`TreeNodeComponent renders sorted children, expanded, leaves and parents
}, },
} }
} }
title="More"
/> />
</div> </div>
</div> </div>

View File

@@ -138,17 +138,19 @@ describe("D3ForceGraph", () => {
it("should call onHighlightedNode callback when mouse hovering over node", () => { it("should call onHighlightedNode callback when mouse hovering over node", () => {
forceGraph.params.onGraphUpdated = () => { forceGraph.params.onGraphUpdated = () => {
const mouseoverEvent = document.createEvent("Events"); if (document) {
mouseoverEvent.initEvent("mouseover", true, false); const mouseoverEvent = document.createEvent("Events");
$(rootNode).find(".node")[0].dispatchEvent(mouseoverEvent); // [0] is v1 vertex mouseoverEvent.initEvent("mouseover", true, false);
expect($(rootNode).find(".node")[0]).toBe(1); $(rootNode).find(".node")[0].dispatchEvent(mouseoverEvent); // [0] is v1 vertex
// onHighlightedNode is always called once to clear the selection // onHighlightedNode is always called once to clear the selection
expect((forceGraph.params.onHighlightedNode as sinon.SinonSpy).calledTwice).toBe(true); expect((forceGraph.params.onHighlightedNode as sinon.SinonSpy).calledTwice).toBe(true);
const onHighlightedNode = (forceGraph.params.onHighlightedNode as sinon.SinonSpy).args[1][0] as D3GraphNodeData; const onHighlightedNode = (forceGraph.params.onHighlightedNode as sinon.SinonSpy)
expect(onHighlightedNode).not.toBe(null); .args[1][0] as D3GraphNodeData;
expect(onHighlightedNode.id).toEqual(v1Id); expect(onHighlightedNode).not.toBe(null);
expect(onHighlightedNode.id).toEqual(v1Id);
}
}; };
forceGraph.updateGraph(newGraph, forceGraph.igraphConfig); forceGraph.updateGraph(newGraph, forceGraph.igraphConfig);

View File

@@ -1084,7 +1084,6 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
public static reportToConsole(type: ConsoleDataType, msg: string, ...errorData: any[]): void | (() => void) { public static reportToConsole(type: ConsoleDataType, msg: string, ...errorData: any[]): void | (() => void) {
let errorDataStr: string = ""; let errorDataStr: string = "";
if (errorData && errorData.length > 0) { if (errorData && errorData.length > 0) {
console.error(msg, errorData);
errorDataStr = ": " + JSON.stringify(errorData); errorDataStr = ": " + JSON.stringify(errorData);
} }

View File

@@ -1,8 +1,8 @@
import * as React from "react"; import * as React from "react";
import { GraphVizComponent, GraphVizComponentProps } from "./GraphVizComponent";
import CollapseArrowIcon from "../../../../images/Collapse_arrow_14x14.svg"; import CollapseArrowIcon from "../../../../images/Collapse_arrow_14x14.svg";
import ExpandIcon from "../../../../images/Expand_14x14.svg"; import ExpandIcon from "../../../../images/Expand_14x14.svg";
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif"; import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
import { GraphVizComponent, GraphVizComponentProps } from "./GraphVizComponent";
interface MiddlePaneComponentProps { interface MiddlePaneComponentProps {
isTabsContentExpanded: boolean; isTabsContentExpanded: boolean;
@@ -17,7 +17,14 @@ export class MiddlePaneComponent extends React.Component<MiddlePaneComponentProp
<div className="middlePane"> <div className="middlePane">
<div className="graphTitle"> <div className="graphTitle">
<span className="paneTitle">Graph</span> <span className="paneTitle">Graph</span>
<span className="graphExpandCollapseBtn pull-right" onClick={this.props.toggleExpandGraph}> <span
className="graphExpandCollapseBtn pull-right"
onClick={this.props.toggleExpandGraph}
role="button"
aria-expanded={this.props.isTabsContentExpanded}
aria-label="View graph in full screen"
tabIndex={0}
>
<img <img
src={this.props.isTabsContentExpanded ? CollapseArrowIcon : ExpandIcon} src={this.props.isTabsContentExpanded ? CollapseArrowIcon : ExpandIcon}
alt={this.props.isTabsContentExpanded ? "collapse graph content" : "expand graph content"} alt={this.props.isTabsContentExpanded ? "collapse graph content" : "expand graph content"}

File diff suppressed because it is too large Load Diff

View File

@@ -129,7 +129,7 @@ export class NotificationConsoleComponent extends React.Component<
className="expandCollapseButton" className="expandCollapseButton"
role="button" role="button"
tabIndex={0} tabIndex={0}
aria-label={"console button" + (this.props.isConsoleExpanded ? " collapsed" : " expanded")} aria-label={"console button" + (this.props.isConsoleExpanded ? " expanded" : " collapsed")}
aria-expanded={!this.props.isConsoleExpanded} aria-expanded={!this.props.isConsoleExpanded}
> >
<img <img
@@ -205,7 +205,9 @@ export class NotificationConsoleComponent extends React.Component<
{item.type === ConsoleDataType.Error && <img className="errorIcon" src={ErrorRedIcon} alt="error" />} {item.type === ConsoleDataType.Error && <img className="errorIcon" src={ErrorRedIcon} alt="error" />}
{item.type === ConsoleDataType.InProgress && <img className="loaderIcon" src={LoaderIcon} alt="in progress" />} {item.type === ConsoleDataType.InProgress && <img className="loaderIcon" src={LoaderIcon} alt="in progress" />}
<span className="date">{item.date}</span> <span className="date">{item.date}</span>
<span className="message">{item.message}</span> <span className="message" role="alert" aria-live="assertive">
{item.message}
</span>
</div> </div>
)); ));
} }

View File

@@ -70,7 +70,7 @@ exports[`NotificationConsoleComponent renders the console 1`] = `
</div> </div>
<div <div
aria-expanded={true} aria-expanded={true}
aria-label="console button expanded" aria-label="console button collapsed"
className="expandCollapseButton" className="expandCollapseButton"
role="button" role="button"
tabIndex={0} tabIndex={0}
@@ -236,7 +236,7 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
</div> </div>
<div <div
aria-expanded={true} aria-expanded={true}
aria-label="console button expanded" aria-label="console button collapsed"
className="expandCollapseButton" className="expandCollapseButton"
role="button" role="button"
tabIndex={0} tabIndex={0}
@@ -340,7 +340,9 @@ exports[`NotificationConsoleComponent renders the console 2`] = `
date date
</span> </span>
<span <span
aria-live="assertive"
className="message" className="message"
role="alert"
> >
message message
</span> </span>

View File

@@ -78,7 +78,7 @@ export default class NotebookManager {
this.notebookContentProvider = new NotebookContentProvider( this.notebookContentProvider = new NotebookContentProvider(
this.inMemoryContentProvider, this.inMemoryContentProvider,
this.gitHubContentProvider, this.gitHubContentProvider,
contents.JupyterContentProvider contents?.JupyterContentProvider
); );
this.notebookClient = new NotebookContainerClient(() => this.notebookClient = new NotebookContainerClient(() =>

View File

@@ -23,10 +23,12 @@ import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneFor
export interface AddDatabasePaneProps { export interface AddDatabasePaneProps {
explorer: Explorer; explorer: Explorer;
buttonElement?: HTMLElement;
} }
export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
explorer: container, explorer: container,
buttonElement,
}: AddDatabasePaneProps) => { }: AddDatabasePaneProps) => {
const closeSidePanel = useSidePanel((state) => state.closeSidePanel); const closeSidePanel = useSidePanel((state) => state.closeSidePanel);
let throughput: number; let throughput: number;
@@ -77,6 +79,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
dataExplorerArea: Constants.Areas.ContextualPane, dataExplorerArea: Constants.Areas.ContextualPane,
}; };
TelemetryProcessor.trace(Action.CreateDatabase, ActionModifiers.Open, addDatabasePaneOpenMessage); TelemetryProcessor.trace(Action.CreateDatabase, ActionModifiers.Open, addDatabasePaneOpenMessage);
buttonElement.focus();
}, []); }, []);
const onSubmit = () => { const onSubmit = () => {

View File

@@ -198,6 +198,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
<Stack className="panelGroupSpacing"> <Stack className="panelGroupSpacing">
<TextField <TextField
aria-required="true" aria-required="true"
required={true}
autoComplete="off" autoComplete="off"
styles={getTextFieldStyles()} styles={getTextFieldStyles()}
pattern="[^/?#\\]*[^/?# \\]" pattern="[^/?#\\]*[^/?# \\]"
@@ -285,6 +286,7 @@ export const CassandraAddCollectionPane: FunctionComponent<CassandraAddCollectio
underlined underlined
styles={getTextFieldStyles({ fontSize: 12, width: 150 })} styles={getTextFieldStyles({ fontSize: 12, width: 150 })}
aria-required="true" aria-required="true"
required={true}
ariaLabel="addCollection-tableId" ariaLabel="addCollection-tableId"
autoComplete="off" autoComplete="off"
pattern="[^/?#\\]*[^/?# \\]" pattern="[^/?#\\]*[^/?# \\]"

View File

@@ -9,6 +9,7 @@
flex-grow: 1; flex-grow: 1;
padding: 0 34px; padding: 0 34px;
margin: 20px 0; margin: 20px 0;
overflow-x: hidden;
& > :not(.collapsibleSection) { & > :not(.collapsibleSection) {
margin-bottom: @DefaultSpace; margin-bottom: @DefaultSpace;

View File

@@ -33,7 +33,13 @@ export const PanelInfoErrorComponent: React.FunctionComponent<PanelInfoErrorProp
<Stack className="panelInfoErrorContainer" horizontal verticalAlign="center"> <Stack className="panelInfoErrorContainer" horizontal verticalAlign="center">
{icon} {icon}
<span className="panelWarningErrorDetailsLinkContainer"> <span className="panelWarningErrorDetailsLinkContainer">
<Text className="panelWarningErrorMessage" variant="small" aria-label="message"> <Text
role="alert"
aria-live="assertive"
aria-label={message}
className="panelWarningErrorMessage"
variant="small"
>
{message} {message}
{link && linkText && ( {link && linkText && (
<Link target="_blank" href={link}> <Link target="_blank" href={link}>

View File

@@ -34,6 +34,6 @@ describe("Right Pane Form", () => {
it("should render error in header", () => { it("should render error in header", () => {
render(<RightPaneForm {...props} formError="file already Exist" />); render(<RightPaneForm {...props} formError="file already Exist" />);
expect(screen.getByLabelText("error")).toBeDefined(); expect(screen.getByLabelText("error")).toBeDefined();
expect(screen.getByLabelText("message").innerHTML).toEqual("file already Exist"); expect(screen.getByRole("alert").innerHTML).toEqual("file already Exist");
}); });
}); });

View File

@@ -29,6 +29,7 @@ describe("Table query select Panel", () => {
it("Should checked availableCheckbox by default", () => { it("Should checked availableCheckbox by default", () => {
const wrapper = mount(<TableQuerySelectPanel {...props} />); const wrapper = mount(<TableQuerySelectPanel {...props} />);
expect(wrapper.find("#availableCheckbox").first().props()).toEqual({ expect(wrapper.find("#availableCheckbox").first().props()).toEqual({
ariaPositionInSet: 0,
id: "availableCheckbox", id: "availableCheckbox",
label: "Available Columns", label: "Available Columns",
checked: true, checked: true,

View File

@@ -128,8 +128,9 @@ export const TableQuerySelectPanel: FunctionComponent<TableQuerySelectPanelProps
label="Available Columns" label="Available Columns"
checked={isAvailableColumnChecked} checked={isAvailableColumnChecked}
onChange={availableColumnsCheckboxClick} onChange={availableColumnsCheckboxClick}
ariaPositionInSet={0}
/> />
{columnOptions.map((column) => { {columnOptions.map((column, index) => {
return ( return (
<Checkbox <Checkbox
label={column.columnName} label={column.columnName}
@@ -137,6 +138,7 @@ export const TableQuerySelectPanel: FunctionComponent<TableQuerySelectPanelProps
key={column.columnName} key={column.columnName}
checked={column.selected} checked={column.selected}
disabled={!column.editable} disabled={!column.editable}
ariaPositionInSet={index + 1}
/> />
); );
})} })}

View File

@@ -37,12 +37,14 @@ exports[`Table query select Panel should render Default properly 1`] = `
className="column-select-view" className="column-select-view"
> >
<StyledCheckboxBase <StyledCheckboxBase
ariaPositionInSet={0}
checked={true} checked={true}
id="availableCheckbox" id="availableCheckbox"
label="Available Columns" label="Available Columns"
onChange={[Function]} onChange={[Function]}
> >
<CheckboxBase <CheckboxBase
ariaPositionInSet={0}
checked={true} checked={true}
id="availableCheckbox" id="availableCheckbox"
label="Available Columns" label="Available Columns"
@@ -328,6 +330,7 @@ exports[`Table query select Panel should render Default properly 1`] = `
<input <input
aria-checked="true" aria-checked="true"
aria-label="Available Columns" aria-label="Available Columns"
aria-posinset={0}
checked={true} checked={true}
className="input-55" className="input-55"
data-ktp-execute-target={true} data-ktp-execute-target={true}
@@ -646,6 +649,7 @@ exports[`Table query select Panel should render Default properly 1`] = `
</CheckboxBase> </CheckboxBase>
</StyledCheckboxBase> </StyledCheckboxBase>
<StyledCheckboxBase <StyledCheckboxBase
ariaPositionInSet={1}
checked={true} checked={true}
disabled={false} disabled={false}
key="" key=""
@@ -653,6 +657,7 @@ exports[`Table query select Panel should render Default properly 1`] = `
onChange={[Function]} onChange={[Function]}
> >
<CheckboxBase <CheckboxBase
ariaPositionInSet={1}
checked={true} checked={true}
disabled={false} disabled={false}
label="" label=""
@@ -939,6 +944,7 @@ exports[`Table query select Panel should render Default properly 1`] = `
aria-checked="true" aria-checked="true"
aria-disabled={false} aria-disabled={false}
aria-label="" aria-label=""
aria-posinset={1}
checked={true} checked={true}
className="input-55" className="input-55"
data-ktp-execute-target={true} data-ktp-execute-target={true}

View File

@@ -327,13 +327,17 @@ exports[`Delete Database Confirmation Pane Should call delete database 1`] = `
key=".0:$.1" key=".0:$.1"
> >
<Text <Text
aria-label="message" aria-label="Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources."
aria-live="assertive"
className="panelWarningErrorMessage" className="panelWarningErrorMessage"
role="alert"
variant="small" variant="small"
> >
<span <span
aria-label="message" aria-label="Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources."
aria-live="assertive"
className="panelWarningErrorMessage css-56" className="panelWarningErrorMessage css-56"
role="alert"
> >
Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources. Warning! The action you are about to take cannot be undone. Continuing will permanently delete this resource and all of its children resources.
</span> </span>

View File

@@ -307,16 +307,23 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
iconSrc: AddDatabaseIcon, iconSrc: AddDatabaseIcon,
title: "New " + getDatabaseName(), title: "New " + getDatabaseName(),
description: undefined, description: undefined,
onClick: () => onClick: () => this.openAddDatabasePanel(),
useSidePanel
.getState()
.openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={this.container} />),
}); });
} }
return items; return items;
} }
private openAddDatabasePanel() {
const newDatabaseButton = document.activeElement as HTMLElement;
useSidePanel
.getState()
.openSidePanel(
"New " + getDatabaseName(),
<AddDatabasePanel explorer={this.container} buttonElement={newDatabaseButton} />
);
}
private decorateOpenCollectionActivity({ databaseId, collectionId }: MostRecentActivity.OpenCollectionItem) { private decorateOpenCollectionActivity({ databaseId, collectionId }: MostRecentActivity.OpenCollectionItem) {
return { return {
iconSrc: NotebookIcon, iconSrc: NotebookIcon,

View File

@@ -999,7 +999,7 @@ export default class QueryTabComponent extends React.Component<IQueryTabComponen
"data-order": 2, "data-order": 2,
"data-title": "Query Stats", "data-title": "Query Stats",
}} }}
style={{ height: "100%" }} style={{ height: "100%", overflowY: "scroll" }}
> >
{this.state.allResultsMetadata.length > 0 && !this.state.error && ( {this.state.allResultsMetadata.length > 0 && !this.state.error && (
<div className="queryMetricsSummaryContainer"> <div className="queryMetricsSummaryContainer">

View File

@@ -47,7 +47,9 @@
<img class="and-or-svg" src="/And-Or.svg" alt="Group selected clauses" /> <img class="and-or-svg" src="/And-Or.svg" alt="Group selected clauses" />
</button> </button>
</th> </th>
<th class="clause-table-cell header-background"><!-- Grouping indicator --></th> <th class="clause-table-cell header-background">
<!-- Grouping indicator -->
</th>
<th class="clause-table-cell header-background and-or-header"> <th class="clause-table-cell header-background and-or-header">
<span data-bind="text: andLabel"></span> <span data-bind="text: andLabel"></span>
</th> </th>
@@ -136,7 +138,7 @@
class="select-options-link" class="select-options-link"
data-bind="click: selectQueryOptions, event: { keydown: onselectQueryOptionsKeyDown }" data-bind="click: selectQueryOptions, event: { keydown: onselectQueryOptionsKeyDown }"
tabindex="0" tabindex="0"
role="link" role="button"
> >
<span>Choose Columns... </span> <span>Choose Columns... </span>
</a> </a>

View File

@@ -85,7 +85,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
}; };
} }
export function hasFlag(flags: string, desiredFlag: string): boolean { export function hasFlag(flags: string | undefined, desiredFlag: string | undefined): boolean {
if (!flags || !desiredFlag) { if (!flags || !desiredFlag) {
return false; return false;
} }

View File

@@ -151,10 +151,10 @@ export const getReadRegions = async (): Promise<Array<string>> => {
}); });
if (response.result.location !== undefined) { if (response.result.location !== undefined) {
readRegions.push(response.result.location.replace(" ", "").toLowerCase()); readRegions.push(response.result.location.split(" ").join("").toLowerCase());
} else { } else {
for (const location of response.result.locations) { for (const location of response.result.locations) {
readRegions.push(location.locationName.replace(" ", "").toLowerCase()); readRegions.push(location.locationName.split(" ").join("").toLowerCase());
} }
} }
return readRegions; return readRegions;

View File

@@ -139,6 +139,14 @@ const getGeneralPath = (subscriptionId: string, resourceGroup: string, name: str
}; };
export const getRegions = async (): Promise<Array<string>> => { export const getRegions = async (): Promise<Array<string>> => {
const telemetryData = {
feature: "Calculate approximate cost",
function: "getRegions",
description: "",
selfServeClassName: SqlX.name,
};
const getRegionsTimestamp = selfServeTraceStart(telemetryData);
try { try {
const regions = new Array<string>(); const regions = new Array<string>();
@@ -156,8 +164,12 @@ export const getRegions = async (): Promise<Array<string>> => {
regions.push(location.locationName.split(" ").join("").toLowerCase()); regions.push(location.locationName.split(" ").join("").toLowerCase());
} }
} }
selfServeTraceSuccess(telemetryData, getRegionsTimestamp);
return regions; return regions;
} catch (err) { } catch (err) {
const failureTelemetry = { err, selfServeClassName: SqlX.name };
selfServeTraceFailure(failureTelemetry, getRegionsTimestamp);
return new Array<string>(); return new Array<string>();
} }
}; };
@@ -167,6 +179,14 @@ const getFetchPricesPathForRegion = (subscriptionId: string): string => {
}; };
export const getPriceMap = async (regions: Array<string>): Promise<Map<string, Map<string, number>>> => { export const getPriceMap = async (regions: Array<string>): Promise<Map<string, Map<string, number>>> => {
const telemetryData = {
feature: "Calculate approximate cost",
function: "getPriceMap",
description: "fetch prices API call",
selfServeClassName: SqlX.name,
};
const getPriceMapTimestamp = selfServeTraceStart(telemetryData);
try { try {
const priceMap = new Map<string, Map<string, number>>(); const priceMap = new Map<string, Map<string, number>>();
@@ -192,8 +212,12 @@ export const getPriceMap = async (regions: Array<string>): Promise<Map<string, M
priceMap.set(region, regionPriceMap); priceMap.set(region, regionPriceMap);
} }
selfServeTraceSuccess(telemetryData, getPriceMapTimestamp);
return priceMap; return priceMap;
} catch (err) { } catch (err) {
const failureTelemetry = { err, selfServeClassName: SqlX.name };
selfServeTraceFailure(failureTelemetry, getPriceMapTimestamp);
return undefined; return undefined;
} }
}; };

View File

@@ -1,5 +1,10 @@
import { IsDisplayable, OnChange, PropertyInfo, RefreshOptions, Values } from "../Decorators"; import { IsDisplayable, OnChange, PropertyInfo, RefreshOptions, Values } from "../Decorators";
import { selfServeTrace } from "../SelfServeTelemetryProcessor"; import {
selfServeTrace,
selfServeTraceFailure,
selfServeTraceStart,
selfServeTraceSuccess,
} from "../SelfServeTelemetryProcessor";
import { import {
ChoiceItem, ChoiceItem,
Description, Description,
@@ -205,6 +210,14 @@ let priceMap: Map<string, Map<string, number>>;
let regions: Array<string>; let regions: Array<string>;
const calculateCost = (skuName: string, instanceCount: number): Description => { const calculateCost = (skuName: string, instanceCount: number): Description => {
const telemetryData = {
feature: "Calculate approximate cost",
function: "calculateCost",
description: "performs final calculation",
selfServeClassName: SqlX.name,
};
const calculateCostTimestamp = selfServeTraceStart(telemetryData);
try { try {
let costPerHour = 0; let costPerHour = 0;
for (const region of regions) { for (const region of regions) {
@@ -215,14 +228,22 @@ const calculateCost = (skuName: string, instanceCount: number): Description => {
costPerHour += incrementalCost; costPerHour += incrementalCost;
} }
if (costPerHour === 0) {
throw new Error("Cost per hour = 0");
}
costPerHour *= instanceCount; costPerHour *= instanceCount;
costPerHour = Math.round(costPerHour * 100) / 100; costPerHour = Math.round(costPerHour * 100) / 100;
selfServeTraceSuccess(telemetryData, calculateCostTimestamp);
return { return {
textTKey: `${costPerHour} USD`, textTKey: `${costPerHour} USD`,
type: DescriptionType.Text, type: DescriptionType.Text,
}; };
} catch (err) { } catch (err) {
const failureTelemetry = { err, regions, priceMap, selfServeClassName: SqlX.name };
selfServeTraceFailure(failureTelemetry, calculateCostTimestamp);
return costPerHourDefaultValue; return costPerHourDefaultValue;
} }
}; };

View File

@@ -1,7 +1,3 @@
import * as Constants from "../Common/Constants";
export const manualToAutoscaleDisclaimer = `The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings and storage of your resource. After autoscale has been enabled, you can change the max RU/s. <a href="${Constants.Urls.autoscaleMigration}">Learn more</a>.`;
export const minAutoPilotThroughput = 4000; export const minAutoPilotThroughput = 4000;
export const autoPilotIncrementStep = 1000; export const autoPilotIncrementStep = 1000;

View File

@@ -1,7 +1,7 @@
import { initializeIcons } from "@fluentui/react";
import { configure } from "enzyme"; import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16"; import Adapter from "enzyme-adapter-react-16";
import "jest-canvas-mock"; import "jest-canvas-mock";
import { initializeIcons } from "@fluentui/react";
import { TextDecoder, TextEncoder } from "util"; import { TextDecoder, TextEncoder } from "util";
configure({ adapter: new Adapter() }); configure({ adapter: new Adapter() });
initializeIcons(); initializeIcons();
@@ -16,3 +16,12 @@ if (typeof window.URL.createObjectURL === "undefined") {
require("jquery-ui-dist/jquery-ui"); require("jquery-ui-dist/jquery-ui");
(<any>global).TextEncoder = TextEncoder; (<any>global).TextEncoder = TextEncoder;
(<any>global).TextDecoder = TextDecoder; (<any>global).TextDecoder = TextDecoder;
// In Node v7 unhandled promise rejections
if (process.env.LISTENING_TO_UNHANDLED_REJECTION !== "true") {
process.on("unhandledRejection", (reason) => {
console.error("reason", reason);
});
// Avoid memory leak by adding too many listeners
process.env.LISTENING_TO_UNHANDLED_REJECTION = "true";
}