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"?>
<svg
width="16"
height="16"
version="1.1"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
<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">
<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"
transform="scale(0.5)"
fill="#000"
stroke="#CCC"
>
<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"
transform="scale(0.5)" fill="#000" stroke="#000">
</path>
</svg>

Before

Width:  |  Height:  |  Size: 503 B

After

Width:  |  Height:  |  Size: 449 B

View File

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

View File

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

View File

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

View File

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

View File

@@ -65,7 +65,7 @@ export interface PriceBreakdown {
currencySign: string;
}
export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14 } };
export const infoAndToolTipTextStyle: ITextStyles = { root: { fontSize: 14, color: "windowtext" } };
export const noLeftPaddingCheckBoxStyle: ICheckboxStyles = {
label: {
@@ -272,7 +272,7 @@ export const manualToAutoscaleDisclaimerElement: JSX.Element = (
<Text styles={infoAndToolTipTextStyle} id="manualToAutoscaleDisclaimerElement">
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={Urls.autoscaleMigration}>Learn more</a>
<Link href={Urls.autoscaleMigration}>Learn more</Link>
</Text>
);

View File

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

View File

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

View File

@@ -20,6 +20,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -39,6 +40,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -73,6 +75,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"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.
<a
<StyledLinkBase
href="https://aka.ms/cosmos-autoscale-migration"
>
Learn more
</a>
</StyledLinkBase>
</Text>
</StyledMessageBar>
<StyledChoiceGroup
@@ -186,6 +189,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -460,6 +464,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}

View File

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

View File

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

View File

@@ -159,6 +159,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"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.
<a
<StyledLinkBase
href="https://aka.ms/cosmos-autoscale-migration"
>
Learn more
</a>
</StyledLinkBase>
</Text>
<Text
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -195,6 +197,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -207,6 +210,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -219,6 +223,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -230,6 +235,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -249,6 +255,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -264,6 +271,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -278,6 +286,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -291,6 +300,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -302,6 +312,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -321,6 +332,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -363,6 +375,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -378,6 +391,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}
@@ -394,6 +408,7 @@ exports[`SettingsUtils functions render 1`] = `
styles={
Object {
"root": Object {
"color": "windowtext",
"fontSize": 14,
},
}

View File

@@ -1,7 +1,6 @@
@import "../../../../less/Common/Constants";
.tabComponentContainer {
overflow: hidden;
height: 100%;
.flex-display();
.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}>
<IconButton
name="More"
title="More"
className="treeMenuEllipsis"
ariaLabel={menuItemLabel}
menuIconProps={{

View File

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

View File

@@ -138,17 +138,19 @@ describe("D3ForceGraph", () => {
it("should call onHighlightedNode callback when mouse hovering over node", () => {
forceGraph.params.onGraphUpdated = () => {
const mouseoverEvent = document.createEvent("Events");
mouseoverEvent.initEvent("mouseover", true, false);
$(rootNode).find(".node")[0].dispatchEvent(mouseoverEvent); // [0] is v1 vertex
expect($(rootNode).find(".node")[0]).toBe(1);
if (document) {
const mouseoverEvent = document.createEvent("Events");
mouseoverEvent.initEvent("mouseover", true, false);
$(rootNode).find(".node")[0].dispatchEvent(mouseoverEvent); // [0] is v1 vertex
// onHighlightedNode is always called once to clear the selection
expect((forceGraph.params.onHighlightedNode as sinon.SinonSpy).calledTwice).toBe(true);
// onHighlightedNode is always called once to clear the selection
expect((forceGraph.params.onHighlightedNode as sinon.SinonSpy).calledTwice).toBe(true);
const onHighlightedNode = (forceGraph.params.onHighlightedNode as sinon.SinonSpy).args[1][0] as D3GraphNodeData;
expect(onHighlightedNode).not.toBe(null);
expect(onHighlightedNode.id).toEqual(v1Id);
const onHighlightedNode = (forceGraph.params.onHighlightedNode as sinon.SinonSpy)
.args[1][0] as D3GraphNodeData;
expect(onHighlightedNode).not.toBe(null);
expect(onHighlightedNode.id).toEqual(v1Id);
}
};
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) {
let errorDataStr: string = "";
if (errorData && errorData.length > 0) {
console.error(msg, errorData);
errorDataStr = ": " + JSON.stringify(errorData);
}

View File

@@ -1,8 +1,8 @@
import * as React from "react";
import { GraphVizComponent, GraphVizComponentProps } from "./GraphVizComponent";
import CollapseArrowIcon from "../../../../images/Collapse_arrow_14x14.svg";
import ExpandIcon from "../../../../images/Expand_14x14.svg";
import LoadingIndicatorIcon from "../../../../images/LoadingIndicator_3Squares.gif";
import { GraphVizComponent, GraphVizComponentProps } from "./GraphVizComponent";
interface MiddlePaneComponentProps {
isTabsContentExpanded: boolean;
@@ -17,7 +17,14 @@ export class MiddlePaneComponent extends React.Component<MiddlePaneComponentProp
<div className="middlePane">
<div className="graphTitle">
<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
src={this.props.isTabsContentExpanded ? CollapseArrowIcon : ExpandIcon}
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"
role="button"
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}
>
<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.InProgress && <img className="loaderIcon" src={LoaderIcon} alt="in progress" />}
<span className="date">{item.date}</span>
<span className="message">{item.message}</span>
<span className="message" role="alert" aria-live="assertive">
{item.message}
</span>
</div>
));
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -33,7 +33,13 @@ export const PanelInfoErrorComponent: React.FunctionComponent<PanelInfoErrorProp
<Stack className="panelInfoErrorContainer" horizontal verticalAlign="center">
{icon}
<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}
{link && linkText && (
<Link target="_blank" href={link}>

View File

@@ -34,6 +34,6 @@ describe("Right Pane Form", () => {
it("should render error in header", () => {
render(<RightPaneForm {...props} formError="file already Exist" />);
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", () => {
const wrapper = mount(<TableQuerySelectPanel {...props} />);
expect(wrapper.find("#availableCheckbox").first().props()).toEqual({
ariaPositionInSet: 0,
id: "availableCheckbox",
label: "Available Columns",
checked: true,

View File

@@ -128,8 +128,9 @@ export const TableQuerySelectPanel: FunctionComponent<TableQuerySelectPanelProps
label="Available Columns"
checked={isAvailableColumnChecked}
onChange={availableColumnsCheckboxClick}
ariaPositionInSet={0}
/>
{columnOptions.map((column) => {
{columnOptions.map((column, index) => {
return (
<Checkbox
label={column.columnName}
@@ -137,6 +138,7 @@ export const TableQuerySelectPanel: FunctionComponent<TableQuerySelectPanelProps
key={column.columnName}
checked={column.selected}
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"
>
<StyledCheckboxBase
ariaPositionInSet={0}
checked={true}
id="availableCheckbox"
label="Available Columns"
onChange={[Function]}
>
<CheckboxBase
ariaPositionInSet={0}
checked={true}
id="availableCheckbox"
label="Available Columns"
@@ -328,6 +330,7 @@ exports[`Table query select Panel should render Default properly 1`] = `
<input
aria-checked="true"
aria-label="Available Columns"
aria-posinset={0}
checked={true}
className="input-55"
data-ktp-execute-target={true}
@@ -646,6 +649,7 @@ exports[`Table query select Panel should render Default properly 1`] = `
</CheckboxBase>
</StyledCheckboxBase>
<StyledCheckboxBase
ariaPositionInSet={1}
checked={true}
disabled={false}
key=""
@@ -653,6 +657,7 @@ exports[`Table query select Panel should render Default properly 1`] = `
onChange={[Function]}
>
<CheckboxBase
ariaPositionInSet={1}
checked={true}
disabled={false}
label=""
@@ -939,6 +944,7 @@ exports[`Table query select Panel should render Default properly 1`] = `
aria-checked="true"
aria-disabled={false}
aria-label=""
aria-posinset={1}
checked={true}
className="input-55"
data-ktp-execute-target={true}

View File

@@ -327,13 +327,17 @@ exports[`Delete Database Confirmation Pane Should call delete database 1`] = `
key=".0:$.1"
>
<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"
role="alert"
variant="small"
>
<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"
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.
</span>

View File

@@ -307,16 +307,23 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
iconSrc: AddDatabaseIcon,
title: "New " + getDatabaseName(),
description: undefined,
onClick: () =>
useSidePanel
.getState()
.openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={this.container} />),
onClick: () => this.openAddDatabasePanel(),
});
}
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) {
return {
iconSrc: NotebookIcon,

View File

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

View File

@@ -47,7 +47,9 @@
<img class="and-or-svg" src="/And-Or.svg" alt="Group selected clauses" />
</button>
</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">
<span data-bind="text: andLabel"></span>
</th>
@@ -136,7 +138,7 @@
class="select-options-link"
data-bind="click: selectQueryOptions, event: { keydown: onselectQueryOptionsKeyDown }"
tabindex="0"
role="link"
role="button"
>
<span>Choose Columns... </span>
</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) {
return false;
}

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
import { initializeIcons } from "@fluentui/react";
import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import "jest-canvas-mock";
import { initializeIcons } from "@fluentui/react";
import { TextDecoder, TextEncoder } from "util";
configure({ adapter: new Adapter() });
initializeIcons();
@@ -16,3 +16,12 @@ if (typeof window.URL.createObjectURL === "undefined") {
require("jquery-ui-dist/jquery-ui");
(<any>global).TextEncoder = TextEncoder;
(<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";
}