mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-26 12:21:23 +00:00
Compare commits
24 Commits
release/bu
...
users/just
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1484a72c8 | ||
|
|
7aeb682bea | ||
|
|
35051bace5 | ||
|
|
4444f66e91 | ||
|
|
5fc53a7f89 | ||
|
|
2180eba0a0 | ||
|
|
ed83bf47e4 | ||
|
|
d657c4919e | ||
|
|
95d33356c3 | ||
|
|
1081432bbd | ||
|
|
ea1f0e9b0c | ||
|
|
44d815454c | ||
|
|
6d604490d3 | ||
|
|
f66b78aaf8 | ||
|
|
34edd96c76 | ||
|
|
7c0aae6ffa | ||
|
|
86e8bf3c80 | ||
|
|
e98c9a83b8 | ||
|
|
7d57a90d50 | ||
|
|
0f896f556b | ||
|
|
30182ed503 | ||
|
|
9bf8cffd29 | ||
|
|
cb92bdb003 | ||
|
|
5f4ea0e614 |
4
.npmrc
4
.npmrc
@@ -1,4 +1,6 @@
|
|||||||
save-exact=true
|
save-exact=true
|
||||||
|
registry=https://msdata.pkgs.visualstudio.com/_packaging/cosmosdbportal/npm/registry/
|
||||||
|
always-auth=true
|
||||||
|
allow-same-version=true
|
||||||
# Ignore peer dependency conflicts
|
# Ignore peer dependency conflicts
|
||||||
force=true # TODO: Remove this when we update to React 17 or higher!
|
force=true # TODO: Remove this when we update to React 17 or higher!
|
||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -24,5 +24,6 @@
|
|||||||
"source.organizeImports": "explicit"
|
"source.organizeImports": "explicit"
|
||||||
},
|
},
|
||||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"sarif-viewer.connectToGithubCodeScanning": "off"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,18 @@ import * as React from "react";
|
|||||||
export interface TooltipProps {
|
export interface TooltipProps {
|
||||||
children: string;
|
children: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
ariaLabelForTooltip?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({ children, className }: TooltipProps) => {
|
export const InfoTooltip: React.FunctionComponent<TooltipProps> = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
ariaLabelForTooltip = children,
|
||||||
|
}: TooltipProps) => {
|
||||||
return (
|
return (
|
||||||
<span className={className}>
|
<span className={className}>
|
||||||
<TooltipHost content={children}>
|
<TooltipHost content={children}>
|
||||||
<Icon iconName="Info" ariaLabel={children} className="panelInfoIcon" tabIndex={0} />
|
<Icon iconName="Info" aria-label={ariaLabelForTooltip} className="panelInfoIcon" tabIndex={0} />
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -143,4 +143,39 @@ describe("SubSettingsComponent", () => {
|
|||||||
expect(subSettingsComponentInstance.getTtlValue(TtlType.On)).toEqual(TtlOn);
|
expect(subSettingsComponentInstance.getTtlValue(TtlType.On)).toEqual(TtlOn);
|
||||||
expect(subSettingsComponentInstance.getTtlValue(TtlType.Off)).toEqual(TtlOff);
|
expect(subSettingsComponentInstance.getTtlValue(TtlType.Off)).toEqual(TtlOff);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uniqueKey is visible", () => {
|
||||||
|
updateUserContext({
|
||||||
|
databaseAccount: {
|
||||||
|
properties: {
|
||||||
|
capabilities: [{ name: "EnableSQL" }],
|
||||||
|
},
|
||||||
|
} as DatabaseAccount,
|
||||||
|
});
|
||||||
|
const subSettingsComponent = new SubSettingsComponent(baseProps);
|
||||||
|
expect(subSettingsComponent.getUniqueKeyVisible()).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uniqueKey not visible due to no keys", () => {
|
||||||
|
const props = {
|
||||||
|
...baseProps,
|
||||||
|
...(baseProps.collection.rawDataModel.uniqueKeyPolicy.uniqueKeys = []),
|
||||||
|
};
|
||||||
|
const subSettingsComponent = new SubSettingsComponent(props);
|
||||||
|
expect(subSettingsComponent.getUniqueKeyVisible()).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uniqueKey not visible for API", () => {
|
||||||
|
const newContainer = new Explorer();
|
||||||
|
updateUserContext({
|
||||||
|
databaseAccount: {
|
||||||
|
properties: {
|
||||||
|
capabilities: [{ name: "EnableMongo" }],
|
||||||
|
},
|
||||||
|
} as DatabaseAccount,
|
||||||
|
});
|
||||||
|
const props = { ...baseProps, container: newContainer };
|
||||||
|
const subSettingsComponent = new SubSettingsComponent(props);
|
||||||
|
expect(subSettingsComponent.getUniqueKeyVisible()).toEqual(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -63,12 +63,16 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
|||||||
private geospatialVisible: boolean;
|
private geospatialVisible: boolean;
|
||||||
private partitionKeyValue: string;
|
private partitionKeyValue: string;
|
||||||
private partitionKeyName: string;
|
private partitionKeyName: string;
|
||||||
|
private uniqueKeyName: string;
|
||||||
|
private uniqueKeyValue: string;
|
||||||
|
|
||||||
constructor(props: SubSettingsComponentProps) {
|
constructor(props: SubSettingsComponentProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.geospatialVisible = userContext.apiType === "SQL";
|
this.geospatialVisible = userContext.apiType === "SQL";
|
||||||
this.partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
|
this.partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
|
||||||
this.partitionKeyValue = this.getPartitionKeyValue();
|
this.partitionKeyValue = this.getPartitionKeyValue();
|
||||||
|
this.uniqueKeyName = "Unique keys";
|
||||||
|
this.uniqueKeyValue = this.getUniqueKeyValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
@@ -351,6 +355,28 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
|||||||
public isLargePartitionKeyEnabled = (): boolean => this.props.collection.partitionKey?.version >= 2;
|
public isLargePartitionKeyEnabled = (): boolean => this.props.collection.partitionKey?.version >= 2;
|
||||||
public isHierarchicalPartitionedContainer = (): boolean => this.props.collection.partitionKey?.kind === "MultiHash";
|
public isHierarchicalPartitionedContainer = (): boolean => this.props.collection.partitionKey?.kind === "MultiHash";
|
||||||
|
|
||||||
|
public getUniqueKeyVisible = (): boolean => {
|
||||||
|
return this.props.collection.rawDataModel.uniqueKeyPolicy?.uniqueKeys.length > 0 && userContext.apiType === "SQL";
|
||||||
|
};
|
||||||
|
|
||||||
|
private getUniqueKeyValue = (): string => {
|
||||||
|
const paths = this.props.collection.rawDataModel.uniqueKeyPolicy?.uniqueKeys?.[0]?.paths;
|
||||||
|
return paths?.join(", ") || "";
|
||||||
|
};
|
||||||
|
|
||||||
|
private getUniqueKeyComponent = (): JSX.Element => (
|
||||||
|
<Stack {...titleAndInputStackProps}>
|
||||||
|
{this.getUniqueKeyVisible() && (
|
||||||
|
<TextField
|
||||||
|
label={this.uniqueKeyName}
|
||||||
|
disabled
|
||||||
|
styles={getTextFieldStyles(undefined, undefined)}
|
||||||
|
defaultValue={this.uniqueKeyValue}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
|
||||||
public render(): JSX.Element {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Stack {...subComponentStackProps}>
|
<Stack {...subComponentStackProps}>
|
||||||
@@ -363,6 +389,8 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
|||||||
{this.props.changeFeedPolicyVisible && this.getChangeFeedComponent()}
|
{this.props.changeFeedPolicyVisible && this.getChangeFeedComponent()}
|
||||||
|
|
||||||
{this.getPartitionKeyComponent()}
|
{this.getPartitionKeyComponent()}
|
||||||
|
|
||||||
|
{this.getUniqueKeyComponent()}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,6 +231,34 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
tokens={
|
||||||
|
{
|
||||||
|
"childrenGap": 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StyledTextFieldBase
|
||||||
|
defaultValue="/id"
|
||||||
|
disabled={true}
|
||||||
|
label="Unique keys"
|
||||||
|
styles={
|
||||||
|
{
|
||||||
|
"fieldGroup": {
|
||||||
|
"borderColor": "",
|
||||||
|
"height": 25,
|
||||||
|
"selectors": {
|
||||||
|
":disabled": {
|
||||||
|
"backgroundColor": undefined,
|
||||||
|
"borderColor": undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"width": 300,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -520,6 +548,34 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
tokens={
|
||||||
|
{
|
||||||
|
"childrenGap": 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StyledTextFieldBase
|
||||||
|
defaultValue="/id"
|
||||||
|
disabled={true}
|
||||||
|
label="Unique keys"
|
||||||
|
styles={
|
||||||
|
{
|
||||||
|
"fieldGroup": {
|
||||||
|
"borderColor": "",
|
||||||
|
"height": 25,
|
||||||
|
"selectors": {
|
||||||
|
":disabled": {
|
||||||
|
"backgroundColor": undefined,
|
||||||
|
"borderColor": undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"width": 300,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -769,6 +825,34 @@ exports[`SubSettingsComponent changeFeedPolicy hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
tokens={
|
||||||
|
{
|
||||||
|
"childrenGap": 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StyledTextFieldBase
|
||||||
|
defaultValue="/id"
|
||||||
|
disabled={true}
|
||||||
|
label="Unique keys"
|
||||||
|
styles={
|
||||||
|
{
|
||||||
|
"fieldGroup": {
|
||||||
|
"borderColor": "",
|
||||||
|
"height": 25,
|
||||||
|
"selectors": {
|
||||||
|
":disabled": {
|
||||||
|
"backgroundColor": undefined,
|
||||||
|
"borderColor": undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"width": 300,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -1083,6 +1167,34 @@ exports[`SubSettingsComponent renders 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
tokens={
|
||||||
|
{
|
||||||
|
"childrenGap": 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StyledTextFieldBase
|
||||||
|
defaultValue="/id"
|
||||||
|
disabled={true}
|
||||||
|
label="Unique keys"
|
||||||
|
styles={
|
||||||
|
{
|
||||||
|
"fieldGroup": {
|
||||||
|
"borderColor": "",
|
||||||
|
"height": 25,
|
||||||
|
"selectors": {
|
||||||
|
":disabled": {
|
||||||
|
"backgroundColor": undefined,
|
||||||
|
"borderColor": undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"width": 300,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -1371,5 +1483,33 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
|||||||
Non-hierarchically partitioned container.
|
Non-hierarchically partitioned container.
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
tokens={
|
||||||
|
{
|
||||||
|
"childrenGap": 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StyledTextFieldBase
|
||||||
|
defaultValue="/id"
|
||||||
|
disabled={true}
|
||||||
|
label="Unique keys"
|
||||||
|
styles={
|
||||||
|
{
|
||||||
|
"fieldGroup": {
|
||||||
|
"borderColor": "",
|
||||||
|
"height": 25,
|
||||||
|
"selectors": {
|
||||||
|
":disabled": {
|
||||||
|
"backgroundColor": undefined,
|
||||||
|
"borderColor": undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"width": 300,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -17,7 +17,15 @@ export const collection = {
|
|||||||
includedPaths: [],
|
includedPaths: [],
|
||||||
excludedPaths: [],
|
excludedPaths: [],
|
||||||
}),
|
}),
|
||||||
uniqueKeyPolicy: {} as DataModels.UniqueKeyPolicy,
|
rawDataModel: {
|
||||||
|
uniqueKeyPolicy: {
|
||||||
|
uniqueKeys: [
|
||||||
|
{
|
||||||
|
paths: ["/id"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
usageSizeInKB: ko.observable(100),
|
usageSizeInKB: ko.observable(100),
|
||||||
offer: ko.observable<DataModels.Offer>({
|
offer: ko.observable<DataModels.Offer>({
|
||||||
autoscaleMaxThroughput: undefined,
|
autoscaleMaxThroughput: undefined,
|
||||||
|
|||||||
@@ -71,8 +71,18 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
|
"rawDataModel": {
|
||||||
|
"uniqueKeyPolicy": {
|
||||||
|
"uniqueKeys": [
|
||||||
|
{
|
||||||
|
"paths": [
|
||||||
|
"/id",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": {},
|
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
@@ -153,8 +163,18 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
|
"rawDataModel": {
|
||||||
|
"uniqueKeyPolicy": {
|
||||||
|
"uniqueKeys": [
|
||||||
|
{
|
||||||
|
"paths": [
|
||||||
|
"/id",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": {},
|
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
@@ -274,8 +294,18 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
|
"rawDataModel": {
|
||||||
|
"uniqueKeyPolicy": {
|
||||||
|
"uniqueKeys": [
|
||||||
|
{
|
||||||
|
"paths": [
|
||||||
|
"/id",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": {},
|
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
@@ -404,8 +434,18 @@ exports[`SettingsComponent renders 1`] = `
|
|||||||
"partitionKeyProperties": [
|
"partitionKeyProperties": [
|
||||||
"partitionKey",
|
"partitionKey",
|
||||||
],
|
],
|
||||||
|
"rawDataModel": {
|
||||||
|
"uniqueKeyPolicy": {
|
||||||
|
"uniqueKeys": [
|
||||||
|
{
|
||||||
|
"paths": [
|
||||||
|
"/id",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
"readSettings": [Function],
|
"readSettings": [Function],
|
||||||
"uniqueKeyPolicy": {},
|
|
||||||
"usageSizeInKB": [Function],
|
"usageSizeInKB": [Function],
|
||||||
"vectorEmbeddingPolicy": [Function],
|
"vectorEmbeddingPolicy": [Function],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,13 +44,18 @@ export const CostEstimateText: FunctionComponent<CostEstimateTextProps> = ({
|
|||||||
const currencySign: string = getCurrencySign(serverId);
|
const currencySign: string = getCurrencySign(serverId);
|
||||||
const multiplier = getMultimasterMultiplier(numberOfRegions, multimasterEnabled);
|
const multiplier = getMultimasterMultiplier(numberOfRegions, multimasterEnabled);
|
||||||
const pricePerRu = isAutoscale ? getAutoscalePricePerRu(serverId, multiplier) : getPricePerRu(serverId, multiplier);
|
const pricePerRu = isAutoscale ? getAutoscalePricePerRu(serverId, multiplier) : getPricePerRu(serverId, multiplier);
|
||||||
|
const estimatedMonthlyCost = "Estimated monthly cost";
|
||||||
|
|
||||||
const iconWithEstimatedCostDisclaimer: JSX.Element = <InfoTooltip>{estimatedCostDisclaimer}</InfoTooltip>;
|
const iconWithEstimatedCostDisclaimer: JSX.Element = (
|
||||||
|
<InfoTooltip ariaLabelForTooltip={`${estimatedMonthlyCost} ${currency} ${estimatedCostDisclaimer}`}>
|
||||||
|
{estimatedCostDisclaimer}
|
||||||
|
</InfoTooltip>
|
||||||
|
);
|
||||||
|
|
||||||
if (isAutoscale) {
|
if (isAutoscale) {
|
||||||
return (
|
return (
|
||||||
<Text variant="small">
|
<Text variant="small">
|
||||||
Estimated monthly cost ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
|
{estimatedMonthlyCost} ({currency}){iconWithEstimatedCostDisclaimer}:{" "}
|
||||||
<b>
|
<b>
|
||||||
{currencySign + calculateEstimateNumber(monthlyPrice / 10)} -{" "}
|
{currencySign + calculateEstimateNumber(monthlyPrice / 10)} -{" "}
|
||||||
{currencySign + calculateEstimateNumber(monthlyPrice)}{" "}
|
{currencySign + calculateEstimateNumber(monthlyPrice)}{" "}
|
||||||
|
|||||||
@@ -345,13 +345,13 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
role="none"
|
role="none"
|
||||||
>
|
>
|
||||||
<StyledIconBase
|
<StyledIconBase
|
||||||
ariaLabel="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
|
aria-label="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<IconBase
|
<IconBase
|
||||||
ariaLabel="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
|
aria-label="Set the throughput — Request Units per second (RU/s) — required for the workload. A read of a 1 KB document uses 1 RU. Select manual if you plan to scale RU/s yourself. Select autoscale to allow the system to scale RU/s based on usage."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
@@ -1358,13 +1358,13 @@ exports[`ThroughputInput Pane should render Default properly 1`] = `
|
|||||||
role="none"
|
role="none"
|
||||||
>
|
>
|
||||||
<StyledIconBase
|
<StyledIconBase
|
||||||
ariaLabel="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
|
aria-label="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<IconBase
|
<IconBase
|
||||||
ariaLabel="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
|
aria-label="Set the max RU/s to the highest RU/s you want your container to scale to. The container will scale between 10% of max RU/s to the max RU/s based on usage."
|
||||||
className="panelInfoIcon"
|
className="panelInfoIcon"
|
||||||
iconName="Info"
|
iconName="Info"
|
||||||
styles={[Function]}
|
styles={[Function]}
|
||||||
|
|||||||
@@ -517,7 +517,6 @@ export function createPostgreButtons(container: Explorer): CommandButtonComponen
|
|||||||
|
|
||||||
export function createVCoreMongoButtons(container: Explorer): CommandButtonComponentProps[] {
|
export function createVCoreMongoButtons(container: Explorer): CommandButtonComponentProps[] {
|
||||||
const openVCoreMongoTerminalButton = createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.VCoreMongo);
|
const openVCoreMongoTerminalButton = createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.VCoreMongo);
|
||||||
|
|
||||||
const addVsCode = createOpenVsCodeDialogButton(container);
|
const addVsCode = createOpenVsCodeDialogButton(container);
|
||||||
return [openVCoreMongoTerminalButton, addVsCode];
|
return [openVCoreMongoTerminalButton, addVsCode];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -608,7 +608,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
|||||||
<RightPaneForm {...genericPaneProps}>
|
<RightPaneForm {...genericPaneProps}>
|
||||||
<div className={`paneMainContent ${styles.container}`}>
|
<div className={`paneMainContent ${styles.container}`}>
|
||||||
{!isFabricNative() && (
|
{!isFabricNative() && (
|
||||||
<Accordion className={`customAccordion ${styles.firstItem}`}>
|
<Accordion className={`customAccordion ${styles.firstItem}`} collapsible>
|
||||||
{shouldShowQueryPageOptions && (
|
{shouldShowQueryPageOptions && (
|
||||||
<AccordionItem value="1">
|
<AccordionItem value="1">
|
||||||
<AccordionHeader>
|
<AccordionHeader>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ exports[`Settings Pane should render Default properly 1`] = `
|
|||||||
>
|
>
|
||||||
<Accordion
|
<Accordion
|
||||||
className="customAccordion ___1uf6361_0000000 fz7g6wx"
|
className="customAccordion ___1uf6361_0000000 fz7g6wx"
|
||||||
|
collapsible={true}
|
||||||
>
|
>
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
value="1"
|
value="1"
|
||||||
@@ -573,6 +574,7 @@ exports[`Settings Pane should render Gremlin properly 1`] = `
|
|||||||
>
|
>
|
||||||
<Accordion
|
<Accordion
|
||||||
className="customAccordion ___1uf6361_0000000 fz7g6wx"
|
className="customAccordion ___1uf6361_0000000 fz7g6wx"
|
||||||
|
collapsible={true}
|
||||||
>
|
>
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
value="7"
|
value="7"
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
value=""
|
value=""
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField is-required root-110"
|
className="ms-TextField is-required root-116"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField-wrapper"
|
className="ms-TextField-wrapper"
|
||||||
@@ -647,7 +647,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
className="ms-Label root-121"
|
className="ms-Label root-127"
|
||||||
htmlFor="TextField0"
|
htmlFor="TextField0"
|
||||||
id="TextFieldLabel2"
|
id="TextFieldLabel2"
|
||||||
>
|
>
|
||||||
@@ -656,13 +656,13 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
</LabelBase>
|
</LabelBase>
|
||||||
</StyledLabelBase>
|
</StyledLabelBase>
|
||||||
<div
|
<div
|
||||||
className="ms-TextField-fieldGroup fieldGroup-111"
|
className="ms-TextField-fieldGroup fieldGroup-117"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-invalid={false}
|
aria-invalid={false}
|
||||||
aria-labelledby="TextFieldLabel2"
|
aria-labelledby="TextFieldLabel2"
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
className="ms-TextField-field field-112"
|
className="ms-TextField-field field-118"
|
||||||
id="TextField0"
|
id="TextField0"
|
||||||
name="collectionIdConfirmation"
|
name="collectionIdConfirmation"
|
||||||
onBlur={[Function]}
|
onBlur={[Function]}
|
||||||
@@ -2464,7 +2464,7 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-label="Create"
|
aria-label="Create"
|
||||||
className="ms-Button ms-Button--primary root-122"
|
className="ms-Button ms-Button--primary root-128"
|
||||||
data-is-focusable={true}
|
data-is-focusable={true}
|
||||||
data-test="Panel/OkButton"
|
data-test="Panel/OkButton"
|
||||||
id="sidePanelOkButton"
|
id="sidePanelOkButton"
|
||||||
@@ -2477,14 +2477,14 @@ exports[`StringInput Pane should render Create new directory properly 1`] = `
|
|||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="ms-Button-flexContainer flexContainer-123"
|
className="ms-Button-flexContainer flexContainer-129"
|
||||||
data-automationid="splitbuttonprimary"
|
data-automationid="splitbuttonprimary"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="ms-Button-textContainer textContainer-124"
|
className="ms-Button-textContainer textContainer-130"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className="ms-Button-label label-126"
|
className="ms-Button-label label-132"
|
||||||
id="id__5"
|
id="id__5"
|
||||||
key="id__5"
|
key="id__5"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ import {
|
|||||||
DetailsList,
|
DetailsList,
|
||||||
DetailsListLayoutMode,
|
DetailsListLayoutMode,
|
||||||
DirectionalHint,
|
DirectionalHint,
|
||||||
|
FontIcon,
|
||||||
IColumn,
|
IColumn,
|
||||||
SelectionMode,
|
SelectionMode,
|
||||||
TooltipHost,
|
TooltipHost,
|
||||||
|
getTheme,
|
||||||
|
mergeStyles,
|
||||||
|
mergeStyleSets,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import { Upload } from "Common/Upload/Upload";
|
import { Upload } from "Common/Upload/Upload";
|
||||||
import { UploadDetailsRecord } from "Contracts/ViewModels";
|
import { UploadDetailsRecord } from "Contracts/ViewModels";
|
||||||
@@ -14,6 +18,36 @@ import { getErrorMessage } from "../../Tables/Utilities";
|
|||||||
import { useSelectedNode } from "../../useSelectedNode";
|
import { useSelectedNode } from "../../useSelectedNode";
|
||||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||||
|
|
||||||
|
const theme = getTheme();
|
||||||
|
const iconClass = mergeStyles({
|
||||||
|
verticalAlign: "middle",
|
||||||
|
maxHeight: "16px",
|
||||||
|
maxWidth: "16px",
|
||||||
|
});
|
||||||
|
|
||||||
|
const classNames = mergeStyleSets({
|
||||||
|
fileIconHeaderIcon: {
|
||||||
|
padding: 0,
|
||||||
|
fontSize: "16px",
|
||||||
|
},
|
||||||
|
fileIconCell: {
|
||||||
|
textAlign: "center",
|
||||||
|
selectors: {
|
||||||
|
"&:before": {
|
||||||
|
content: ".",
|
||||||
|
display: "inline-block",
|
||||||
|
verticalAlign: "middle",
|
||||||
|
height: "100%",
|
||||||
|
width: "0px",
|
||||||
|
visibility: "hidden",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
error: [{ color: theme.semanticColors.errorIcon }, iconClass],
|
||||||
|
accept: [{ color: theme.semanticColors.successIcon }, iconClass],
|
||||||
|
warning: [{ color: theme.semanticColors.warningIcon }, iconClass],
|
||||||
|
});
|
||||||
|
|
||||||
export const UploadItemsPane: FunctionComponent = () => {
|
export const UploadItemsPane: FunctionComponent = () => {
|
||||||
const [files, setFiles] = useState<FileList>();
|
const [files, setFiles] = useState<FileList>();
|
||||||
const [uploadFileData, setUploadFileData] = useState<UploadDetailsRecord[]>([]);
|
const [uploadFileData, setUploadFileData] = useState<UploadDetailsRecord[]>([]);
|
||||||
@@ -60,44 +94,94 @@ export const UploadItemsPane: FunctionComponent = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const columns: IColumn[] = [
|
const columns: IColumn[] = [
|
||||||
|
{
|
||||||
|
key: "icons",
|
||||||
|
name: "",
|
||||||
|
fieldName: "",
|
||||||
|
className: classNames.fileIconCell,
|
||||||
|
iconClassName: classNames.fileIconHeaderIcon,
|
||||||
|
isIconOnly: true,
|
||||||
|
minWidth: 16,
|
||||||
|
maxWidth: 16,
|
||||||
|
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
||||||
|
if (item.numFailed) {
|
||||||
|
const errorList = (
|
||||||
|
<ul
|
||||||
|
aria-label={"error list"}
|
||||||
|
style={{
|
||||||
|
margin: "5px 0",
|
||||||
|
paddingLeft: "20px",
|
||||||
|
listStyleType: "disc", // Explicitly set to use bullets (dots)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.errors.map((error, i) => (
|
||||||
|
<li key={i} style={{ display: "list-item" }}>
|
||||||
|
{error}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TooltipHost
|
||||||
|
content={errorList}
|
||||||
|
id={`tooltip-${index}-${column.key}`}
|
||||||
|
directionalHint={DirectionalHint.bottomAutoEdge}
|
||||||
|
>
|
||||||
|
<FontIcon iconName="Error" className={classNames.error} aria-label="error" />
|
||||||
|
</TooltipHost>
|
||||||
|
);
|
||||||
|
} else if (item.numThrottled) {
|
||||||
|
return <FontIcon iconName="Warning" className={classNames.warning} aria-label="warning" />;
|
||||||
|
} else {
|
||||||
|
return <FontIcon iconName="Accept" className={classNames.accept} aria-label="accept" />;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "fileName",
|
key: "fileName",
|
||||||
name: "FILE NAME",
|
name: "FILE NAME",
|
||||||
fieldName: "fileName",
|
fieldName: "fileName",
|
||||||
minWidth: 140,
|
minWidth: 120,
|
||||||
maxWidth: 140,
|
maxWidth: 140,
|
||||||
|
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
||||||
|
const fieldContent = item.fileName;
|
||||||
|
return (
|
||||||
|
<TooltipHost
|
||||||
|
content={fieldContent}
|
||||||
|
id={`tooltip-${index}-${column.key}`}
|
||||||
|
directionalHint={DirectionalHint.bottomAutoEdge}
|
||||||
|
>
|
||||||
|
{fieldContent}
|
||||||
|
</TooltipHost>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "status",
|
key: "status",
|
||||||
name: "STATUS",
|
name: "STATUS",
|
||||||
fieldName: "numSucceeded",
|
fieldName: "numSucceeded",
|
||||||
minWidth: 140,
|
minWidth: 120,
|
||||||
maxWidth: 140,
|
maxWidth: 140,
|
||||||
isRowHeader: true,
|
isRowHeader: true,
|
||||||
isResizable: true,
|
isResizable: true,
|
||||||
data: "string",
|
data: "string",
|
||||||
isPadded: true,
|
isPadded: true,
|
||||||
|
onRender: (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
||||||
|
const fieldContent = `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`;
|
||||||
|
return (
|
||||||
|
<TooltipHost
|
||||||
|
content={fieldContent}
|
||||||
|
id={`tooltip-${index}-${column.key}`}
|
||||||
|
directionalHint={DirectionalHint.bottomAutoEdge}
|
||||||
|
>
|
||||||
|
{fieldContent}
|
||||||
|
</TooltipHost>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const _renderItemColumn = (item: UploadDetailsRecord, index: number, column: IColumn) => {
|
|
||||||
let fieldContent: string;
|
|
||||||
const tooltipId = `tooltip-${index}-${column.key}`;
|
|
||||||
|
|
||||||
switch (column.key) {
|
|
||||||
case "status":
|
|
||||||
fieldContent = `${item.numSucceeded} created, ${item.numThrottled} throttled, ${item.numFailed} errors`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fieldContent = item.fileName;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<TooltipHost content={fieldContent} id={tooltipId} directionalHint={DirectionalHint.rightCenter}>
|
|
||||||
{fieldContent}
|
|
||||||
</TooltipHost>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RightPaneForm {...props}>
|
<RightPaneForm {...props}>
|
||||||
<div className="paneMainContent">
|
<div className="paneMainContent">
|
||||||
@@ -115,7 +199,6 @@ export const UploadItemsPane: FunctionComponent = () => {
|
|||||||
<DetailsList
|
<DetailsList
|
||||||
items={uploadFileData}
|
items={uploadFileData}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
onRenderItemColumn={_renderItemColumn}
|
|
||||||
selectionMode={SelectionMode.none}
|
selectionMode={SelectionMode.none}
|
||||||
layoutMode={DetailsListLayoutMode.justified}
|
layoutMode={DetailsListLayoutMode.justified}
|
||||||
isHeaderVisible={true}
|
isHeaderVisible={true}
|
||||||
|
|||||||
@@ -30,8 +30,10 @@ import { KeyboardAction, KeyboardActionGroup, KeyboardActionHandler, useKeyboard
|
|||||||
import { isFabric, isFabricMirrored, isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil";
|
import { isFabric, isFabricMirrored, isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
import { getCollectionName, getDatabaseName } from "Utils/APITypeUtils";
|
import { getCollectionName, getDatabaseName } from "Utils/APITypeUtils";
|
||||||
|
import { conditionalClass } from "Utils/StyleUtils";
|
||||||
import { Allotment, AllotmentHandle } from "allotment";
|
import { Allotment, AllotmentHandle } from "allotment";
|
||||||
import { useSidePanel } from "hooks/useSidePanel";
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
|
import useZoomLevel from "hooks/useZoomLevel";
|
||||||
import { debounce } from "lodash";
|
import { debounce } from "lodash";
|
||||||
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
@@ -104,6 +106,23 @@ const useSidebarStyles = makeStyles({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
accessibleContent: {
|
||||||
|
"@media (max-width: 420px)": {
|
||||||
|
overflow: "scroll",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
minHeightResponsive: {
|
||||||
|
"@media (max-width: 420px)": {
|
||||||
|
minHeight: "400px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
accessibleContentZoom: {
|
||||||
|
overflow: "scroll",
|
||||||
|
},
|
||||||
|
|
||||||
|
minHeightZoom: {
|
||||||
|
minHeight: "400px",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
interface GlobalCommandsProps {
|
interface GlobalCommandsProps {
|
||||||
@@ -275,6 +294,7 @@ export const SidebarContainer: React.FC<SidebarProps> = ({ explorer }) => {
|
|||||||
const [expandedSize, setExpandedSize] = React.useState(300);
|
const [expandedSize, setExpandedSize] = React.useState(300);
|
||||||
const hasSidebar = userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo";
|
const hasSidebar = userContext.apiType !== "Postgres" && userContext.apiType !== "VCoreMongo";
|
||||||
const allotment = useRef<AllotmentHandle>(null);
|
const allotment = useRef<AllotmentHandle>(null);
|
||||||
|
const isZoomed = useZoomLevel();
|
||||||
|
|
||||||
const expand = useCallback(() => {
|
const expand = useCallback(() => {
|
||||||
if (!expanded) {
|
if (!expanded) {
|
||||||
@@ -325,11 +345,23 @@ export const SidebarContainer: React.FC<SidebarProps> = ({ explorer }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="sidebarContainer">
|
<div className="sidebarContainer">
|
||||||
<Allotment ref={allotment} onChange={onChange} onDragEnd={onDragEnd} className="resourceTreeAndTabs">
|
<Allotment
|
||||||
|
ref={allotment}
|
||||||
|
onChange={onChange}
|
||||||
|
onDragEnd={onDragEnd}
|
||||||
|
className={`resourceTreeAndTabs ${styles.accessibleContent} ${conditionalClass(
|
||||||
|
isZoomed,
|
||||||
|
styles.accessibleContentZoom,
|
||||||
|
)}`}
|
||||||
|
>
|
||||||
{/* Collections Tree - Start */}
|
{/* Collections Tree - Start */}
|
||||||
{hasSidebar && (
|
{hasSidebar && (
|
||||||
// When collapsed, we force the pane to 24 pixels wide and make it non-resizable.
|
// When collapsed, we force the pane to 24 pixels wide and make it non-resizable.
|
||||||
<Allotment.Pane minSize={24} preferredSize={250}>
|
<Allotment.Pane
|
||||||
|
className={`${styles.minHeightResponsive} ${conditionalClass(isZoomed, styles.minHeightZoom)}`}
|
||||||
|
minSize={24}
|
||||||
|
preferredSize={250}
|
||||||
|
>
|
||||||
<CosmosFluentProvider className={mergeClasses(styles.sidebar)}>
|
<CosmosFluentProvider className={mergeClasses(styles.sidebar)}>
|
||||||
<div className={styles.sidebarContainer}>
|
<div className={styles.sidebarContainer}>
|
||||||
{loading && (
|
{loading && (
|
||||||
@@ -385,7 +417,10 @@ export const SidebarContainer: React.FC<SidebarProps> = ({ explorer }) => {
|
|||||||
</CosmosFluentProvider>
|
</CosmosFluentProvider>
|
||||||
</Allotment.Pane>
|
</Allotment.Pane>
|
||||||
)}
|
)}
|
||||||
<Allotment.Pane minSize={200}>
|
<Allotment.Pane
|
||||||
|
className={`${styles.minHeightResponsive} ${conditionalClass(isZoomed, styles.minHeightZoom)}`}
|
||||||
|
minSize={200}
|
||||||
|
>
|
||||||
<Tabs explorer={explorer} />
|
<Tabs explorer={explorer} />
|
||||||
</Allotment.Pane>
|
</Allotment.Pane>
|
||||||
</Allotment>
|
</Allotment>
|
||||||
|
|||||||
@@ -291,10 +291,10 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
<form className="connectExplorerFormContainer">
|
<form className="connectExplorerFormContainer">
|
||||||
<div className="splashScreenContainer">
|
<div className="splashScreenContainer">
|
||||||
<div className="splashScreen">
|
<div className="splashScreen">
|
||||||
<h1 className="title" role="heading" aria-label={title}>
|
<h2 className="title" role="heading" aria-label={title}>
|
||||||
{title}
|
{title}
|
||||||
<FeaturePanelLauncher />
|
<FeaturePanelLauncher />
|
||||||
</h1>
|
</h2>
|
||||||
<div className="subtitle">{subtitle}</div>
|
<div className="subtitle">{subtitle}</div>
|
||||||
{this.getSplashScreenButtons()}
|
{this.getSplashScreenButtons()}
|
||||||
{useCarousel.getState().showCoachMark && (
|
{useCarousel.getState().showCoachMark && (
|
||||||
|
|||||||
@@ -144,6 +144,13 @@ export const useDocumentsTabStyles = makeStyles({
|
|||||||
deleteProgressContent: {
|
deleteProgressContent: {
|
||||||
paddingTop: tokens.spacingVerticalL,
|
paddingTop: tokens.spacingVerticalL,
|
||||||
},
|
},
|
||||||
|
smallScreenContent: {
|
||||||
|
"@media (max-width: 420px)": {
|
||||||
|
flexWrap: "wrap",
|
||||||
|
minHeight: "max-content",
|
||||||
|
padding: "4px",
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export class DocumentsTabV2 extends TabsBase {
|
export class DocumentsTabV2 extends TabsBase {
|
||||||
@@ -729,7 +736,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
|||||||
} else if (result.statusCode >= 400) {
|
} else if (result.statusCode >= 400) {
|
||||||
newFailed.push(result.documentId);
|
newFailed.push(result.documentId);
|
||||||
logConsoleError(
|
logConsoleError(
|
||||||
`Failed to delete document ${result.documentId.id} with status code ${result.statusCode}`,
|
`Failed to delete document ${result.documentId.id()} with status code ${result.statusCode}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -2102,7 +2109,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
|
|||||||
return (
|
return (
|
||||||
<CosmosFluentProvider className={styles.container}>
|
<CosmosFluentProvider className={styles.container}>
|
||||||
<div data-test={"DocumentsTab"} className="tab-pane active" role="tabpanel" style={{ display: "flex" }}>
|
<div data-test={"DocumentsTab"} className="tab-pane active" role="tabpanel" style={{ display: "flex" }}>
|
||||||
<div data-test={"DocumentsTab/Filter"} className={styles.filterRow}>
|
<div data-test={"DocumentsTab/Filter"} className={`${styles.filterRow} ${styles.smallScreenContent}`}>
|
||||||
{!isPreferredApiMongoDB && <span> SELECT * FROM c </span>}
|
{!isPreferredApiMongoDB && <span> SELECT * FROM c </span>}
|
||||||
<InputDataList
|
<InputDataList
|
||||||
dropdownOptions={getFilterChoices()}
|
dropdownOptions={getFilterChoices()}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ exports[`Documents tab (noSql API) when rendered should render the page 1`] = `
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="___11ktxfv_0000000 f1o614cb fy9rknc f22iagw fsnqrgy f1f5gg8d fjodcmx f122n59 f1f09k3d fg706s2 frpde29"
|
className="___11ktxfv_0000000 f1o614cb fy9rknc f22iagw fsnqrgy f1f5gg8d fjodcmx f122n59 f1f09k3d fg706s2 frpde29 ___1ngl8o6_0000000 fz7mnu6 fl3egqs flhmrkm"
|
||||||
data-test="DocumentsTab/Filter"
|
data-test="DocumentsTab/Filter"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import RunQuery from "../../../../images/RunQuery.png";
|
|||||||
import { QueryResults } from "../../../Contracts/ViewModels";
|
import { QueryResults } from "../../../Contracts/ViewModels";
|
||||||
import { ErrorList } from "./ErrorList";
|
import { ErrorList } from "./ErrorList";
|
||||||
import { ResultsView } from "./ResultsView";
|
import { ResultsView } from "./ResultsView";
|
||||||
|
import useZoomLevel from "hooks/useZoomLevel";
|
||||||
|
import { conditionalClass } from "Utils/StyleUtils";
|
||||||
|
|
||||||
export interface ResultsViewProps {
|
export interface ResultsViewProps {
|
||||||
isMongoDB: boolean;
|
isMongoDB: boolean;
|
||||||
@@ -23,11 +25,16 @@ interface QueryResultProps extends ResultsViewProps {
|
|||||||
|
|
||||||
const ExecuteQueryCallToAction: React.FC = () => {
|
const ExecuteQueryCallToAction: React.FC = () => {
|
||||||
const styles = useQueryTabStyles();
|
const styles = useQueryTabStyles();
|
||||||
|
const isZoomed = useZoomLevel();
|
||||||
return (
|
return (
|
||||||
<div data-test="QueryTab/ResultsPane/ExecuteCTA" className={styles.executeCallToAction}>
|
<div data-test="QueryTab/ResultsPane/ExecuteCTA" className={styles.executeCallToAction}>
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
<img src={RunQuery} aria-hidden="true" />
|
<img
|
||||||
|
className={`${styles.responsiveImg} ${conditionalClass(isZoomed, styles.zoomedImageSize)}`}
|
||||||
|
src={RunQuery}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
<p>Execute a query to see the results</p>
|
<p>Execute a query to see the results</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ export const useQueryTabStyles = makeStyles({
|
|||||||
height: "100%",
|
height: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
|
"@media (max-width: 420px)": {
|
||||||
|
overflow: "scroll",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
queryResultsMessage: {
|
queryResultsMessage: {
|
||||||
...shorthands.margin("5px"),
|
...shorthands.margin("5px"),
|
||||||
@@ -38,6 +41,9 @@ export const useQueryTabStyles = makeStyles({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
rowGap: "12px",
|
rowGap: "12px",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
|
"@media (max-width: 420px)": {
|
||||||
|
height: "auto",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
queryResultsTabContentContainer: {
|
queryResultsTabContentContainer: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@@ -93,4 +99,12 @@ export const useQueryTabStyles = makeStyles({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
},
|
},
|
||||||
|
responsiveImg: {
|
||||||
|
"@media (max-width: 420px)": {
|
||||||
|
width: "50px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
zoomedImageSize: {
|
||||||
|
width: "60px",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1122,6 +1122,9 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
stats.numSucceeded++;
|
stats.numSucceeded++;
|
||||||
} else if (response.statusCode === 429) {
|
} else if (response.statusCode === 429) {
|
||||||
documentsToAttempt.push(attemptedDocuments[index]);
|
documentsToAttempt.push(attemptedDocuments[index]);
|
||||||
|
} else if (response.statusCode === 409) {
|
||||||
|
stats.numFailed++;
|
||||||
|
stats.errors.push(`Document with id ${attemptedDocuments[index].id} already exists.`);
|
||||||
} else {
|
} else {
|
||||||
stats.numFailed++;
|
stats.numFailed++;
|
||||||
stats.errors.push(JSON.stringify(response.resourceBody));
|
stats.errors.push(JSON.stringify(response.resourceBody));
|
||||||
|
|||||||
@@ -21,3 +21,11 @@ export function copyStyles(sourceDoc: Document, targetDoc: Document): void {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conditionally returns a class name based on a boolean condition.
|
||||||
|
* If the condition is true, returns the `trueValue` class; otherwise, returns `falseValue` (or an empty string if not provided).
|
||||||
|
*/
|
||||||
|
export function conditionalClass(condition: boolean, trueValue: string, falseValue?: string): string {
|
||||||
|
return condition ? trueValue : falseValue || "";
|
||||||
|
}
|
||||||
|
|||||||
20
src/hooks/useZoomLevel.ts
Normal file
20
src/hooks/useZoomLevel.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
const useZoomLevel = (threshold: number = 2): boolean => {
|
||||||
|
const [isZoomed, setIsZoomed] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkZoom = () => {
|
||||||
|
const zoomLevel = window.devicePixelRatio;
|
||||||
|
setIsZoomed(zoomLevel >= threshold);
|
||||||
|
};
|
||||||
|
|
||||||
|
checkZoom();
|
||||||
|
window.addEventListener("resize", checkZoom);
|
||||||
|
return () => window.removeEventListener("resize", checkZoom);
|
||||||
|
}, [threshold]);
|
||||||
|
|
||||||
|
return isZoomed;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useZoomLevel;
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<clear />
|
<clear />
|
||||||
<add name="X-Xss-Protection" value="1; mode=block" />
|
<add name="X-Xss-Protection" value="1; mode=block" />
|
||||||
<add name="X-Content-Type-Options" value="nosniff" />
|
<add name="X-Content-Type-Options" value="nosniff" />
|
||||||
<add name="Content-Security-Policy" value="frame-ancestors 'self' portal.azure.com *.portal.azure.com portal.azure.us portal.azure.cn portal.microsoftazure.de df.onecloud.azure-test.net *.fabric.microsoft.com *.powerbi.com *.analysis-df.windows.net dataexplorer-preview.azurewebsites.net" />
|
<add name="Content-Security-Policy" value="frame-src 'vscode:' frame-ancestors 'self' portal.azure.com *.portal.azure.com portal.azure.us portal.azure.cn portal.microsoftazure.de df.onecloud.azure-test.net *.fabric.microsoft.com *.powerbi.com *.analysis-df.windows.net dataexplorer-preview.azurewebsites.net" />
|
||||||
</customHeaders>
|
</customHeaders>
|
||||||
<redirectHeaders>
|
<redirectHeaders>
|
||||||
<clear />
|
<clear />
|
||||||
|
|||||||
Reference in New Issue
Block a user