mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-29 22:02:01 +00:00
Compare commits
7 Commits
refresh-ar
...
users/saks
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91cd61d387 | ||
|
|
6167f94bc3 | ||
|
|
be89c634f3 | ||
|
|
e9151bcaf0 | ||
|
|
72debd0778 | ||
|
|
42e230b88b | ||
|
|
6196ba4722 |
@@ -128,7 +128,7 @@
|
||||
@provisionDatabaseThroughputInfo: 200px;
|
||||
|
||||
//tabs container
|
||||
@ActiveTabHeight: 31px;
|
||||
@ActiveTabHeight: 32px;
|
||||
@ActiveTabWidth: 141px;
|
||||
@TabsHeight: 30px;
|
||||
@TabsWidth: 140px;
|
||||
|
||||
@@ -406,7 +406,11 @@ body {
|
||||
width: 440px;
|
||||
min-height: 565px;
|
||||
}
|
||||
|
||||
.dataExplorerLoaderforcopyJobs{
|
||||
width: 100%;
|
||||
min-height: 565px;
|
||||
right: 0;
|
||||
}
|
||||
.dataExplorerTabLoaderContainer {
|
||||
left: initial;
|
||||
top: initial;
|
||||
@@ -2643,7 +2647,7 @@ a:link {
|
||||
|
||||
.tabPanesContainer {
|
||||
flex-grow: 1;
|
||||
overflow-y: scroll;
|
||||
overflow: hidden;
|
||||
background-color: var(--colorNeutralBackground1);
|
||||
color: var(--colorNeutralForeground1);
|
||||
}
|
||||
@@ -2651,6 +2655,7 @@ a:link {
|
||||
.tabs-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.paddingspan4 {
|
||||
@@ -2677,7 +2682,7 @@ a:link {
|
||||
width: @ActiveTabWidth;
|
||||
}
|
||||
|
||||
.nav-tabs > li.active .contentWrapper {
|
||||
.nav-tabs > li.active .contentWrapper .tabNavText {
|
||||
border-bottom: 2px solid var(--colorCompoundBrandBackground);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ html {
|
||||
body {
|
||||
font-family: @FabricFont;
|
||||
background-color: #f5f5f5;
|
||||
--colorCompoundBrandBackground: @FabricAccentMedium;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -41,7 +42,7 @@ a:focus {
|
||||
}
|
||||
|
||||
.nav-tabs-margin {
|
||||
padding-top: 5px;
|
||||
padding-top: 0px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
@@ -68,17 +69,20 @@ a:focus {
|
||||
}
|
||||
|
||||
.nav-tabs > li > .tabNavContentContainer > .tab_Content:hover {
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content,
|
||||
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content:hover {
|
||||
border-bottom: 2px solid @FabricAccentMedium;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.nav-tabs > li.active > .tabNavContentContainer > .tab_Content > .contentWrapper > .tabNavText {
|
||||
border-bottom: 0px none transparent;
|
||||
}
|
||||
.nav-tabs > li.active .contentWrapper .tabNavText {
|
||||
border-bottom: 2px solid @FabricAccentMedium;
|
||||
}
|
||||
|
||||
.tabNavContentContainer {
|
||||
padding: @SmallSpace 0px @SmallSpace 0px;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Overlay, Spinner, SpinnerSize } from "@fluentui/react";
|
||||
import { useThemeStore } from "hooks/useTheme";
|
||||
import React from "react";
|
||||
|
||||
interface LoadingOverlayProps {
|
||||
@@ -7,6 +8,7 @@ interface LoadingOverlayProps {
|
||||
}
|
||||
|
||||
const LoadingOverlay: React.FC<LoadingOverlayProps> = ({ isLoading, label }) => {
|
||||
const isDarkMode = useThemeStore((state) => state.isDarkMode);
|
||||
if (!isLoading) {
|
||||
return null;
|
||||
}
|
||||
@@ -15,7 +17,7 @@ const LoadingOverlay: React.FC<LoadingOverlayProps> = ({ isLoading, label }) =>
|
||||
<Overlay
|
||||
styles={{
|
||||
root: {
|
||||
backgroundColor: "rgba(255,255,255,0.9)",
|
||||
backgroundColor: isDarkMode ? "rgba(32, 31, 30, 0.9)" : "rgba(255,255,255,0.9)",
|
||||
zIndex: 9999,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
@@ -23,7 +25,11 @@ const LoadingOverlay: React.FC<LoadingOverlayProps> = ({ isLoading, label }) =>
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Spinner size={SpinnerSize.large} label={label} styles={{ label: { fontWeight: 600 } }} />
|
||||
<Spinner
|
||||
size={SpinnerSize.large}
|
||||
label={label}
|
||||
styles={{ label: { fontWeight: 600, color: isDarkMode ? "#ffffff" : "#323130" } }}
|
||||
/>
|
||||
</Overlay>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,3 +11,14 @@
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Override dark mode inherit for pagination icons */
|
||||
body.isDarkMode .pager-container .ms-Button .ms-Button-icon,
|
||||
body.isDarkMode .pager-container .ms-Button i {
|
||||
color: var(--colorBrandForeground1);
|
||||
}
|
||||
|
||||
body.isDarkMode .pager-container .ms-Button:disabled .ms-Button-icon,
|
||||
body.isDarkMode .pager-container .ms-Button:disabled i {
|
||||
color: var(--colorNeutralForegroundDisabled);
|
||||
}
|
||||
@@ -31,6 +31,7 @@ const iconButtonStyles = {
|
||||
outline: "none",
|
||||
},
|
||||
};
|
||||
const textStyle: React.CSSProperties = { color: "var(--colorNeutralForeground1)" };
|
||||
|
||||
const Pager: React.FC<PagerProps> = ({
|
||||
startIndex,
|
||||
@@ -59,7 +60,7 @@ const Pager: React.FC<PagerProps> = ({
|
||||
return (
|
||||
<div className={className || "pager-container"}>
|
||||
{showItemCount && (
|
||||
<Text>
|
||||
<Text style={textStyle}>
|
||||
Showing {startIndex + 1} - {endIndex} of {totalCount} items
|
||||
</Text>
|
||||
)}
|
||||
@@ -82,7 +83,7 @@ const Pager: React.FC<PagerProps> = ({
|
||||
disabled={disabled || currentPage === 1}
|
||||
styles={iconButtonStyles}
|
||||
/>
|
||||
<Text>
|
||||
<Text style={textStyle}>
|
||||
Page {currentPage} of {totalPages}
|
||||
</Text>
|
||||
<IconButton
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
import { CommandBar as FluentCommandBar, ICommandBarItemProps } from "@fluentui/react";
|
||||
import React from "react";
|
||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||
import { useThemeStore } from "../../../hooks/useTheme";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import * as CommandBarUtil from "../../Menus/CommandBar/CommandBarUtil";
|
||||
import { getThemeTokens } from "../../Theme/ThemeUtil";
|
||||
import { ContainerCopyProps } from "../Types/CopyJobTypes";
|
||||
import { getCommandBarButtons } from "./Utils";
|
||||
|
||||
const backgroundColor = StyleConstants.BaseLight;
|
||||
const rootStyle = {
|
||||
root: {
|
||||
backgroundColor: backgroundColor,
|
||||
},
|
||||
};
|
||||
|
||||
const CopyJobCommandBar: React.FC<ContainerCopyProps> = ({ explorer }) => {
|
||||
const isDarkMode = useThemeStore((state) => state.isDarkMode);
|
||||
const themeTokens = getThemeTokens(isDarkMode);
|
||||
const backgroundColor = themeTokens.colorNeutralBackground1;
|
||||
|
||||
const rootStyle = {
|
||||
root: {
|
||||
backgroundColor: backgroundColor,
|
||||
},
|
||||
};
|
||||
|
||||
const commandBarItems: CommandButtonComponentProps[] = getCommandBarButtons(explorer);
|
||||
const controlButtons: ICommandBarItemProps[] = CommandBarUtil.convertButton(commandBarItems, backgroundColor);
|
||||
|
||||
return (
|
||||
<div className="commandBarContainer">
|
||||
<div className="commandBarContainer" style={{ backgroundColor }}>
|
||||
<FluentCommandBar
|
||||
ariaLabel="Use left and right arrow keys to navigate between commands"
|
||||
styles={rootStyle}
|
||||
|
||||
@@ -82,9 +82,9 @@ describe("CommandBar Utils", () => {
|
||||
it("should include feedback button when platform is Portal", () => {
|
||||
const buttons = getCommandBarButtons(mockExplorer);
|
||||
|
||||
expect(buttons.length).toBe(3);
|
||||
expect(buttons.length).toBe(4);
|
||||
|
||||
const feedbackButton = buttons[2];
|
||||
const feedbackButton = buttons[3];
|
||||
expect(feedbackButton).toBeDefined();
|
||||
expect(feedbackButton.ariaLabel).toBe("Provide feedback on copy jobs");
|
||||
expect(feedbackButton.tooltipText).toBe("Feedback");
|
||||
@@ -107,7 +107,7 @@ describe("CommandBar Utils", () => {
|
||||
const { getCommandBarButtons: getCommandBarButtonsEmulator } = await import("./Utils");
|
||||
const buttons = getCommandBarButtonsEmulator(mockExplorer);
|
||||
|
||||
expect(buttons.length).toBe(2);
|
||||
expect(buttons.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should call openCreateCopyJobPanel when create button is clicked", () => {
|
||||
@@ -131,7 +131,7 @@ describe("CommandBar Utils", () => {
|
||||
|
||||
it("should call openContainerCopyFeedbackBlade when feedback button is clicked", () => {
|
||||
const buttons = getCommandBarButtons(mockExplorer);
|
||||
const feedbackButton = buttons[2];
|
||||
const feedbackButton = buttons[3];
|
||||
|
||||
feedbackButton.onCommandClick({} as React.SyntheticEvent);
|
||||
|
||||
@@ -148,7 +148,10 @@ describe("CommandBar Utils", () => {
|
||||
expect(buttons[1].iconAlt).toBe("Refresh");
|
||||
|
||||
expect(buttons[2].iconSrc).toBeDefined();
|
||||
expect(buttons[2].iconAlt).toBe("Feedback");
|
||||
expect(buttons[2].iconAlt).toBe("Dark Theme");
|
||||
|
||||
expect(buttons[3].iconSrc).toBeDefined();
|
||||
expect(buttons[3].iconAlt).toBe("Feedback");
|
||||
});
|
||||
|
||||
it("should handle null MonitorCopyJobsRefState ref gracefully", () => {
|
||||
@@ -202,12 +205,13 @@ describe("CommandBar Utils", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should maintain button order: create, refresh, feedback", () => {
|
||||
it("should maintain button order: create, refresh, themeToggle, feedback", () => {
|
||||
const buttons = getCommandBarButtons(mockExplorer);
|
||||
|
||||
expect(buttons[0].tooltipText).toBe("Create Copy Job");
|
||||
expect(buttons[1].tooltipText).toBe("Refresh");
|
||||
expect(buttons[2].tooltipText).toBe("Feedback");
|
||||
expect(buttons[2].tooltipText).toBe("Dark Theme");
|
||||
expect(buttons[3].tooltipText).toBe("Feedback");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -229,7 +233,7 @@ describe("CommandBar Utils", () => {
|
||||
buttons[1].onCommandClick({} as React.SyntheticEvent);
|
||||
expect(mockRefreshJobList).toHaveBeenCalled();
|
||||
|
||||
buttons[2].onCommandClick({} as React.SyntheticEvent);
|
||||
buttons[3].onCommandClick({} as React.SyntheticEvent);
|
||||
expect(mockOpenContainerCopyFeedbackBlade).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import AddIcon from "../../../../images/Add.svg";
|
||||
import FeedbackIcon from "../../../../images/Feedback-Command.svg";
|
||||
import MoonIcon from "../../../../images/MoonIcon.svg";
|
||||
import RefreshIcon from "../../../../images/refresh-cosmos.svg";
|
||||
import SunIcon from "../../../../images/SunIcon.svg";
|
||||
import { configContext, Platform } from "../../../ConfigContext";
|
||||
import { useThemeStore } from "../../../hooks/useTheme";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import Explorer from "../../Explorer";
|
||||
import * as Actions from "../Actions/CopyJobActions";
|
||||
@@ -11,6 +14,7 @@ import { CopyJobCommandBarBtnType } from "../Types/CopyJobTypes";
|
||||
|
||||
function getCopyJobBtns(explorer: Explorer): CopyJobCommandBarBtnType[] {
|
||||
const monitorCopyJobsRef = MonitorCopyJobsRefState((state) => state.ref);
|
||||
const isDarkMode = useThemeStore.getState().isDarkMode;
|
||||
const buttons: CopyJobCommandBarBtnType[] = [
|
||||
{
|
||||
key: "createCopyJob",
|
||||
@@ -26,7 +30,15 @@ function getCopyJobBtns(explorer: Explorer): CopyJobCommandBarBtnType[] {
|
||||
ariaLabel: ContainerCopyMessages.refreshButtonAriaLabel,
|
||||
onClick: () => monitorCopyJobsRef?.refreshJobList(),
|
||||
},
|
||||
{
|
||||
key: "themeToggle",
|
||||
iconSrc: isDarkMode ? SunIcon : MoonIcon,
|
||||
label: isDarkMode ? "Light Theme" : "Dark Theme",
|
||||
ariaLabel: isDarkMode ? "Switch to Light Theme" : "Switch to Dark Theme",
|
||||
onClick: () => useThemeStore.getState().toggleTheme(),
|
||||
},
|
||||
];
|
||||
|
||||
if (configContext.platform === Platform.Portal) {
|
||||
buttons.push({
|
||||
key: "feedback",
|
||||
|
||||
@@ -12,7 +12,12 @@ import useToggle from "./hooks/useToggle";
|
||||
const managedIdentityTooltip = (
|
||||
<Text>
|
||||
{ContainerCopyMessages.addManagedIdentity.tooltip.content}
|
||||
<Link href={ContainerCopyMessages.addManagedIdentity.tooltip.href} target="_blank" rel="noopener noreferrer">
|
||||
<Link
|
||||
style={{ color: "var(--colorBrandForeground1)" }}
|
||||
href={ContainerCopyMessages.addManagedIdentity.tooltip.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{ContainerCopyMessages.addManagedIdentity.tooltip.hrefText}
|
||||
</Link>
|
||||
</Text>
|
||||
@@ -26,7 +31,7 @@ const AddManagedIdentity: React.FC<AddManagedIdentityProps> = () => {
|
||||
|
||||
return (
|
||||
<Stack className="addManagedIdentityContainer" tokens={{ childrenGap: 15, padding: "0 0 0 20px" }}>
|
||||
<Text>
|
||||
<Text className="themeText">
|
||||
{ContainerCopyMessages.addManagedIdentity.description} 
|
||||
<Link href={ContainerCopyMessages.addManagedIdentity.descriptionHref} target="_blank" rel="noopener noreferrer">
|
||||
{ContainerCopyMessages.addManagedIdentity.descriptionHrefText}
|
||||
|
||||
@@ -13,7 +13,12 @@ import useToggle from "./hooks/useToggle";
|
||||
const TooltipContent = (
|
||||
<Text>
|
||||
{ContainerCopyMessages.readPermissionAssigned.tooltip.content}
|
||||
<Link href={ContainerCopyMessages.readPermissionAssigned.tooltip.href} target="_blank" rel="noopener noreferrer">
|
||||
<Link
|
||||
style={{ color: "var(--colorBrandForeground1)" }}
|
||||
href={ContainerCopyMessages.readPermissionAssigned.tooltip.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{ContainerCopyMessages.readPermissionAssigned.tooltip.hrefText}
|
||||
</Link>
|
||||
</Text>
|
||||
|
||||
@@ -47,8 +47,8 @@ const PermissionGroup: React.FC<PermissionGroupConfig> = ({ title, description,
|
||||
tokens={{ childrenGap: 15 }}
|
||||
styles={{
|
||||
root: {
|
||||
background: "#fafafa",
|
||||
border: "1px solid #e1e1e1",
|
||||
background: "var(--colorNeutralBackground2)",
|
||||
border: "1px solid var(--colorNeutralStroke1)",
|
||||
borderRadius: 8,
|
||||
padding: 16,
|
||||
boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
|
||||
@@ -56,11 +56,11 @@ const PermissionGroup: React.FC<PermissionGroupConfig> = ({ title, description,
|
||||
}}
|
||||
>
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
<Text variant="medium" style={{ fontWeight: 600 }}>
|
||||
<Text variant="medium" style={{ fontWeight: 600, color: "var(--colorNeutralForeground1)" }}>
|
||||
{title}
|
||||
</Text>
|
||||
{description && (
|
||||
<Text variant="small" styles={{ root: { color: "#605E5C" } }}>
|
||||
<Text variant="small" styles={{ root: { color: "var(--colorNeutralForeground2)" } }}>
|
||||
{description}
|
||||
</Text>
|
||||
)}
|
||||
@@ -100,7 +100,7 @@ const AssignPermissions = () => {
|
||||
|
||||
return (
|
||||
<Stack className="assignPermissionsContainer" tokens={{ childrenGap: 20 }}>
|
||||
<Text variant="medium">
|
||||
<Text variant="medium" style={{ color: "var(--colorNeutralForeground1)" }}>
|
||||
{isSameAccount && copyJobState.migrationType === CopyJobMigrationType.Online
|
||||
? ContainerCopyMessages.assignPermissions.intraAccountOnlineDescription(
|
||||
copyJobState?.source?.account?.name || "",
|
||||
|
||||
@@ -12,7 +12,12 @@ import useToggle from "./hooks/useToggle";
|
||||
const managedIdentityTooltip = (
|
||||
<Text>
|
||||
{ContainerCopyMessages.defaultManagedIdentity.tooltip.content}
|
||||
<Link href={ContainerCopyMessages.defaultManagedIdentity.tooltip.href} target="_blank" rel="noopener noreferrer">
|
||||
<Link
|
||||
style={{ color: "var(--colorBrandForeground1)" }}
|
||||
href={ContainerCopyMessages.defaultManagedIdentity.tooltip.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{ContainerCopyMessages.defaultManagedIdentity.tooltip.hrefText}
|
||||
</Link>
|
||||
</Text>
|
||||
|
||||
@@ -13,7 +13,12 @@ import InfoTooltip from "../Components/InfoTooltip";
|
||||
const tooltipContent = (
|
||||
<Text>
|
||||
{ContainerCopyMessages.pointInTimeRestore.tooltip.content}
|
||||
<Link href={ContainerCopyMessages.pointInTimeRestore.tooltip.href} target="_blank" rel="noopener noreferrer">
|
||||
<Link
|
||||
style={{ color: "var(--colorBrandForeground1)" }}
|
||||
href={ContainerCopyMessages.pointInTimeRestore.tooltip.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{ContainerCopyMessages.pointInTimeRestore.tooltip.hrefText}
|
||||
</Link>
|
||||
</Text>
|
||||
|
||||
@@ -5,7 +5,7 @@ exports[`AddManagedIdentity Snapshot Tests renders initial state correctly 1`] =
|
||||
class="ms-Stack addManagedIdentityContainer css-109"
|
||||
>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
>
|
||||
A system-assigned managed identity is restricted to one per resource and is tied to the lifecycle of this resource. Once enabled, you can grant permissions to the managed identity by using Azure role-based access control (Azure RBAC). The managed identity is authenticated with Microsoft Entra ID, so you don’t have to store any credentials in code.
|
||||
|
||||
@@ -92,7 +92,7 @@ exports[`AddManagedIdentity Snapshot Tests renders loading state 1`] = `
|
||||
class="ms-Stack addManagedIdentityContainer css-109"
|
||||
>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
>
|
||||
A system-assigned managed identity is restricted to one per resource and is tied to the lifecycle of this resource. Once enabled, you can grant permissions to the managed identity by using Azure role-based access control (Azure RBAC). The managed identity is authenticated with Microsoft Entra ID, so you don’t have to store any credentials in code.
|
||||
|
||||
@@ -192,13 +192,13 @@ exports[`AddManagedIdentity Snapshot Tests renders loading state 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="css-124"
|
||||
class="themeText css-124"
|
||||
style="font-weight: 600;"
|
||||
>
|
||||
Enable system assigned managed identity
|
||||
</span>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
>
|
||||
Enable system-assigned managed identity on the test-target-account. To confirm, click the "Yes" button.
|
||||
</span>
|
||||
@@ -261,7 +261,7 @@ exports[`AddManagedIdentity Snapshot Tests renders with toggle on and popover vi
|
||||
class="ms-Stack addManagedIdentityContainer css-109"
|
||||
>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
>
|
||||
A system-assigned managed identity is restricted to one per resource and is tied to the lifecycle of this resource. Once enabled, you can grant permissions to the managed identity by using Azure role-based access control (Azure RBAC). The managed identity is authenticated with Microsoft Entra ID, so you don’t have to store any credentials in code.
|
||||
|
||||
@@ -345,13 +345,13 @@ exports[`AddManagedIdentity Snapshot Tests renders with toggle on and popover vi
|
||||
style="max-width: 450px;"
|
||||
>
|
||||
<span
|
||||
class="css-124"
|
||||
class="themeText css-124"
|
||||
style="font-weight: 600;"
|
||||
>
|
||||
Enable system assigned managed identity
|
||||
</span>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
>
|
||||
Enable system-assigned managed identity on the test-target-account. To confirm, click the "Yes" button.
|
||||
</span>
|
||||
|
||||
@@ -22,10 +22,10 @@ const PopoverContainer: React.FC<PopoverContainerProps> = React.memo(
|
||||
style={{ maxWidth: 450 }}
|
||||
>
|
||||
<LoadingOverlay isLoading={isLoading} label={ContainerCopyMessages.popoverOverlaySpinnerLabel} />
|
||||
<Text variant="mediumPlus" style={{ fontWeight: 600 }}>
|
||||
<Text variant="mediumPlus" className="themeText" style={{ fontWeight: 600 }}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text>{children}</Text>
|
||||
<Text className="themeText">{children}</Text>
|
||||
<Stack horizontal tokens={{ childrenGap: 20 }}>
|
||||
<PrimaryButton text={"Yes"} onClick={onPrimary} disabled={isLoading} />
|
||||
<DefaultButton text="No" onClick={onCancel} disabled={isLoading} />
|
||||
|
||||
@@ -7,11 +7,11 @@ exports[`PopoverMessage Component Edge Cases should handle empty string title 1`
|
||||
style="max-width: 450px;"
|
||||
>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
style="font-weight: 600;"
|
||||
/>
|
||||
<span
|
||||
class="css-111"
|
||||
class="themeText css-111"
|
||||
>
|
||||
<div>
|
||||
Test content
|
||||
@@ -74,7 +74,7 @@ exports[`PopoverMessage Component Edge Cases should handle null children 1`] = `
|
||||
style="max-width: 450px;"
|
||||
>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
style="font-weight: 600;"
|
||||
>
|
||||
Test Title
|
||||
@@ -136,7 +136,7 @@ exports[`PopoverMessage Component Edge Cases should handle undefined children 1`
|
||||
style="max-width: 450px;"
|
||||
>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
style="font-weight: 600;"
|
||||
>
|
||||
Test Title
|
||||
@@ -198,13 +198,13 @@ exports[`PopoverMessage Component Edge Cases should handle very long title 1`] =
|
||||
style="max-width: 450px;"
|
||||
>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
style="font-weight: 600;"
|
||||
>
|
||||
This is a very long title that might cause layout issues or text wrapping in the popover component
|
||||
</span>
|
||||
<span
|
||||
class="css-111"
|
||||
class="themeText css-111"
|
||||
>
|
||||
<div>
|
||||
Test content
|
||||
@@ -269,13 +269,13 @@ exports[`PopoverMessage Component Rendering should render correctly when visible
|
||||
style="max-width: 450px;"
|
||||
>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
style="font-weight: 600;"
|
||||
>
|
||||
Test Title
|
||||
</span>
|
||||
<span
|
||||
class="css-111"
|
||||
class="themeText css-111"
|
||||
>
|
||||
<div>
|
||||
Test content
|
||||
@@ -338,13 +338,13 @@ exports[`PopoverMessage Component Rendering should render correctly with differe
|
||||
style="max-width: 450px;"
|
||||
>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
style="font-weight: 600;"
|
||||
>
|
||||
Test Title
|
||||
</span>
|
||||
<span
|
||||
class="css-111"
|
||||
class="themeText css-111"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
@@ -412,13 +412,13 @@ exports[`PopoverMessage Component Rendering should render correctly with differe
|
||||
style="max-width: 450px;"
|
||||
>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
style="font-weight: 600;"
|
||||
>
|
||||
Custom Title
|
||||
</span>
|
||||
<span
|
||||
class="css-111"
|
||||
class="themeText css-111"
|
||||
>
|
||||
<div>
|
||||
Test content
|
||||
@@ -485,13 +485,13 @@ exports[`PopoverMessage Component Rendering should render correctly with loading
|
||||
data-testid="loading-overlay"
|
||||
/>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
style="font-weight: 600;"
|
||||
>
|
||||
Test Title
|
||||
</span>
|
||||
<span
|
||||
class="css-111"
|
||||
class="themeText css-111"
|
||||
>
|
||||
<div>
|
||||
Test content
|
||||
|
||||
@@ -41,7 +41,7 @@ const AddCollectionPanelWrapper: React.FunctionComponent<AddCollectionPanelWrapp
|
||||
return (
|
||||
<Stack className="addCollectionPanelWrapper">
|
||||
<Stack.Item className="addCollectionPanelHeader">
|
||||
<Text>{ContainerCopyMessages.createNewContainerSubHeading}</Text>
|
||||
<Text className="themeText">{ContainerCopyMessages.createNewContainerSubHeading}</Text>
|
||||
</Stack.Item>
|
||||
<Stack.Item className="addCollectionPanelBody">
|
||||
<AddCollectionPanel explorer={explorer} isCopyJobFlow={true} onSubmitSuccess={handleAddCollectionSuccess} />
|
||||
|
||||
@@ -9,7 +9,7 @@ exports[`AddCollectionPanelWrapper Component Rendering should match snapshot 1`]
|
||||
class="ms-StackItem addCollectionPanelHeader css-110"
|
||||
>
|
||||
<span
|
||||
class="css-111"
|
||||
class="themeText css-111"
|
||||
>
|
||||
Select the properties for your container.
|
||||
</span>
|
||||
@@ -50,7 +50,7 @@ exports[`AddCollectionPanelWrapper Component Rendering should match snapshot wit
|
||||
class="ms-StackItem addCollectionPanelHeader css-110"
|
||||
>
|
||||
<span
|
||||
class="css-111"
|
||||
class="themeText css-111"
|
||||
>
|
||||
Select the properties for your container.
|
||||
</span>
|
||||
@@ -91,7 +91,7 @@ exports[`AddCollectionPanelWrapper Component Rendering should match snapshot wit
|
||||
class="ms-StackItem addCollectionPanelHeader css-110"
|
||||
>
|
||||
<span
|
||||
class="css-111"
|
||||
class="themeText css-111"
|
||||
>
|
||||
Select the properties for your container.
|
||||
</span>
|
||||
@@ -132,7 +132,7 @@ exports[`AddCollectionPanelWrapper Component Rendering should match snapshot wit
|
||||
class="ms-StackItem addCollectionPanelHeader css-110"
|
||||
>
|
||||
<span
|
||||
class="css-111"
|
||||
class="themeText css-111"
|
||||
>
|
||||
Select the properties for your container.
|
||||
</span>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
/* eslint-disable react/display-name */
|
||||
import { Checkbox, Stack } from "@fluentui/react";
|
||||
import { Checkbox, ICheckboxStyles, Stack } from "@fluentui/react";
|
||||
import React from "react";
|
||||
import ContainerCopyMessages from "../../../../ContainerCopyMessages";
|
||||
|
||||
@@ -9,8 +9,25 @@ interface MigrationTypeCheckboxProps {
|
||||
onChange: (_ev?: React.FormEvent, checked?: boolean) => void;
|
||||
}
|
||||
|
||||
export const MigrationTypeCheckbox: React.FC<MigrationTypeCheckboxProps> = React.memo(({ checked, onChange }) => (
|
||||
<Stack horizontal horizontalAlign="space-between" className="migrationTypeRow">
|
||||
<Checkbox label={ContainerCopyMessages.migrationTypeCheckboxLabel} checked={checked} onChange={onChange} />
|
||||
</Stack>
|
||||
));
|
||||
const checkboxStyles: ICheckboxStyles = {
|
||||
text: { color: "var(--colorNeutralForeground1)" },
|
||||
checkbox: { borderColor: "var(--colorNeutralStroke1)" },
|
||||
root: {
|
||||
selectors: {
|
||||
":hover .ms-Checkbox-text": { color: "var(--colorNeutralForeground1)" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const MigrationTypeCheckbox: React.FC<MigrationTypeCheckboxProps> = React.memo(({ checked, onChange }) => {
|
||||
return (
|
||||
<Stack horizontal horizontalAlign="space-between" className="migrationTypeRow">
|
||||
<Checkbox
|
||||
label={ContainerCopyMessages.migrationTypeCheckboxLabel}
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
styles={checkboxStyles}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ const SelectAccount = React.memo(() => {
|
||||
|
||||
return (
|
||||
<Stack data-test="Panel:SelectAccountContainer" className="selectAccountContainer" tokens={{ childrenGap: 15 }}>
|
||||
<Text>{ContainerCopyMessages.selectAccountDescription}</Text>
|
||||
<Text className="themeText">{ContainerCopyMessages.selectAccountDescription}</Text>
|
||||
|
||||
<SubscriptionDropdown />
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ exports[`SelectAccount Component Rendering should render correctly with snapshot
|
||||
data-test="Panel:SelectAccountContainer"
|
||||
>
|
||||
<span
|
||||
class="css-110"
|
||||
class="themeText css-110"
|
||||
>
|
||||
Please select a source account from which to copy.
|
||||
</span>
|
||||
|
||||
@@ -48,7 +48,7 @@ const SelectSourceAndTargetContainers = ({ showAddCollectionPanel }: SelectSourc
|
||||
|
||||
return (
|
||||
<Stack className="selectSourceAndTargetContainers" tokens={{ childrenGap: 25 }}>
|
||||
<span>{ContainerCopyMessages.selectSourceAndTargetContainersDescription}</span>
|
||||
<span className="themeText">{ContainerCopyMessages.selectSourceAndTargetContainersDescription}</span>
|
||||
<DatabaseContainerSection
|
||||
heading={ContainerCopyMessages.sourceContainerSubHeading}
|
||||
databaseOptions={sourceDatabaseOptions}
|
||||
|
||||
@@ -41,7 +41,11 @@ export const DatabaseContainerSection = ({
|
||||
onChange={containerOnChange}
|
||||
/>
|
||||
{handleOnDemandCreateContainer && (
|
||||
<ActionButton className="create-container-link-btn" onClick={() => handleOnDemandCreateContainer()}>
|
||||
<ActionButton
|
||||
className="create-container-link-btn"
|
||||
style={{ color: "var(--colorBrandForeground1)" }}
|
||||
onClick={() => handleOnDemandCreateContainer()}
|
||||
>
|
||||
{ContainerCopyMessages.createContainerButtonLabel}
|
||||
</ActionButton>
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DetailsList, DetailsListLayoutMode, IColumn, Stack, Text } from "@fluentui/react";
|
||||
import React, { memo } from "react";
|
||||
import { useThemeStore } from "../../../../hooks/useTheme";
|
||||
import ContainerCopyMessages from "../../ContainerCopyMessages";
|
||||
import { CopyJobStatusType } from "../../Enums/CopyJobEnums";
|
||||
import { CopyJobType } from "../../Types/CopyJobTypes";
|
||||
@@ -63,6 +64,19 @@ const getCopyJobDetailsListColumns = (): IColumn[] => {
|
||||
};
|
||||
|
||||
const CopyJobDetails: React.FC<CopyJobDetailsProps> = ({ job }) => {
|
||||
const isDarkMode = useThemeStore((state) => state.isDarkMode);
|
||||
|
||||
const errorMessageStyle: React.CSSProperties = {
|
||||
whiteSpace: "pre-wrap",
|
||||
...(isDarkMode && {
|
||||
whiteSpace: "pre-wrap",
|
||||
backgroundColor: "var(--colorNeutralBackground2)",
|
||||
color: "var(--colorNeutralForeground1)",
|
||||
padding: "10px",
|
||||
borderRadius: "4px",
|
||||
}),
|
||||
};
|
||||
|
||||
const selectedContainers = [
|
||||
{
|
||||
sourceContainerName: job?.Source?.containerName || "N/A",
|
||||
@@ -77,10 +91,10 @@ const CopyJobDetails: React.FC<CopyJobDetailsProps> = ({ job }) => {
|
||||
<Stack className="copyJobDetailsContainer" tokens={{ childrenGap: 15 }} data-testid="copy-job-details">
|
||||
{job.Error ? (
|
||||
<Stack.Item data-testid="error-stack" style={sectionCss.verticalAlign}>
|
||||
<Text className="bold" style={sectionCss.headingText}>
|
||||
<Text className="bold themeText" style={sectionCss.headingText}>
|
||||
{ContainerCopyMessages.errorTitle}
|
||||
</Text>
|
||||
<Text as="pre" style={{ whiteSpace: "pre-wrap" }}>
|
||||
<Text as="pre" style={errorMessageStyle}>
|
||||
{job.Error.message}
|
||||
</Text>
|
||||
</Stack.Item>
|
||||
@@ -88,16 +102,16 @@ const CopyJobDetails: React.FC<CopyJobDetailsProps> = ({ job }) => {
|
||||
<Stack.Item data-testid="selectedcollection-stack">
|
||||
<Stack tokens={{ childrenGap: 15 }}>
|
||||
<Stack.Item style={sectionCss.verticalAlign}>
|
||||
<Text className="bold">{ContainerCopyMessages.MonitorJobs.Columns.lastUpdatedTime}</Text>
|
||||
<Text>{job.LastUpdatedTime}</Text>
|
||||
<Text className="bold themeText">{ContainerCopyMessages.MonitorJobs.Columns.lastUpdatedTime}</Text>
|
||||
<Text className="themeText">{job.LastUpdatedTime}</Text>
|
||||
</Stack.Item>
|
||||
<Stack.Item style={sectionCss.verticalAlign}>
|
||||
<Text className="bold">{ContainerCopyMessages.sourceAccountLabel}</Text>
|
||||
<Text>{job.Source?.remoteAccountName}</Text>
|
||||
<Text className="bold themeText">{ContainerCopyMessages.sourceAccountLabel}</Text>
|
||||
<Text className="themeText">{job.Source?.remoteAccountName}</Text>
|
||||
</Stack.Item>
|
||||
<Stack.Item style={sectionCss.verticalAlign}>
|
||||
<Text className="bold">{ContainerCopyMessages.MonitorJobs.Columns.mode}</Text>
|
||||
<Text>{job.Mode}</Text>
|
||||
<Text className="bold themeText">{ContainerCopyMessages.MonitorJobs.Columns.mode}</Text>
|
||||
<Text className="themeText">{job.Mode}</Text>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
|
||||
@@ -1,30 +1,14 @@
|
||||
import { FontIcon, getTheme, mergeStyles, mergeStyleSets, Spinner, SpinnerSize, Stack, Text } from "@fluentui/react";
|
||||
import { FontIcon, mergeStyles, Spinner, SpinnerSize, Stack, Text } from "@fluentui/react";
|
||||
import PropTypes from "prop-types";
|
||||
import React from "react";
|
||||
import ContainerCopyMessages from "../../ContainerCopyMessages";
|
||||
import { CopyJobStatusType } from "../../Enums/CopyJobEnums";
|
||||
|
||||
const theme = getTheme();
|
||||
|
||||
const iconClass = mergeStyles({
|
||||
fontSize: "16px",
|
||||
marginRight: "8px",
|
||||
});
|
||||
|
||||
const classNames = mergeStyleSets({
|
||||
[CopyJobStatusType.Pending]: [{ color: theme.semanticColors.bodySubtext }, iconClass],
|
||||
[CopyJobStatusType.InProgress]: [{ color: theme.palette.themePrimary }, iconClass],
|
||||
[CopyJobStatusType.Running]: [{ color: theme.palette.themePrimary }, iconClass],
|
||||
[CopyJobStatusType.Partitioning]: [{ color: theme.palette.themePrimary }, iconClass],
|
||||
[CopyJobStatusType.Paused]: [{ color: theme.palette.themePrimary }, iconClass],
|
||||
[CopyJobStatusType.Skipped]: [{ color: theme.semanticColors.bodySubtext }, iconClass],
|
||||
[CopyJobStatusType.Cancelled]: [{ color: theme.semanticColors.bodySubtext }, iconClass],
|
||||
[CopyJobStatusType.Failed]: [{ color: theme.semanticColors.errorIcon }, iconClass],
|
||||
[CopyJobStatusType.Faulted]: [{ color: theme.semanticColors.errorIcon }, iconClass],
|
||||
[CopyJobStatusType.Completed]: [{ color: theme.semanticColors.successIcon }, iconClass],
|
||||
unknown: [{ color: theme.semanticColors.bodySubtext }, iconClass],
|
||||
});
|
||||
|
||||
const iconMap: Partial<Record<CopyJobStatusType, string>> = {
|
||||
[CopyJobStatusType.Pending]: "Clock",
|
||||
[CopyJobStatusType.Paused]: "CirclePause",
|
||||
@@ -35,6 +19,17 @@ const iconMap: Partial<Record<CopyJobStatusType, string>> = {
|
||||
[CopyJobStatusType.Completed]: "CompletedSolid",
|
||||
};
|
||||
|
||||
// Icon colors for different statuses
|
||||
const statusIconColors: Partial<Record<CopyJobStatusType, string>> = {
|
||||
[CopyJobStatusType.Failed]: "var(--colorPaletteRedForeground1)",
|
||||
[CopyJobStatusType.Faulted]: "var(--colorPaletteRedForeground1)",
|
||||
[CopyJobStatusType.Completed]: "#107c10", // Green color for success
|
||||
[CopyJobStatusType.InProgress]: "var(--colorBrandForeground1)",
|
||||
[CopyJobStatusType.Running]: "var(--colorBrandForeground1)",
|
||||
[CopyJobStatusType.Partitioning]: "var(--colorBrandForeground1)",
|
||||
[CopyJobStatusType.Paused]: "var(--colorBrandForeground1)",
|
||||
};
|
||||
|
||||
export interface CopyJobStatusWithIconProps {
|
||||
status: CopyJobStatusType;
|
||||
}
|
||||
@@ -47,19 +42,17 @@ const CopyJobStatusWithIcon: React.FC<CopyJobStatusWithIconProps> = React.memo((
|
||||
CopyJobStatusType.InProgress,
|
||||
CopyJobStatusType.Partitioning,
|
||||
].includes(status);
|
||||
const iconColor = statusIconColors[status] || "var(--colorNeutralForeground2)";
|
||||
const iconStyle = mergeStyles(iconClass, { color: iconColor });
|
||||
|
||||
return (
|
||||
<Stack horizontal verticalAlign="center">
|
||||
{isSpinnerStatus ? (
|
||||
<Spinner size={SpinnerSize.small} style={{ marginRight: "8px" }} />
|
||||
) : (
|
||||
<FontIcon
|
||||
aria-label={status}
|
||||
iconName={iconMap[status] || "UnknownSolid"}
|
||||
className={classNames[status] || classNames.unknown}
|
||||
/>
|
||||
<FontIcon aria-label={status} iconName={iconMap[status] || "UnknownSolid"} className={iconStyle} />
|
||||
)}
|
||||
<Text>{statusText}</Text>
|
||||
<Text className="themeText">{statusText}</Text>
|
||||
</Stack>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
} from "@fluentui/react";
|
||||
import React, { useEffect } from "react";
|
||||
import Pager from "../../../../Common/Pager";
|
||||
import { useThemeStore } from "../../../../hooks/useTheme";
|
||||
import { getThemeTokens } from "../../../Theme/ThemeUtil";
|
||||
import { openCopyJobDetailsPanel } from "../../Actions/CopyJobActions";
|
||||
import { CopyJobType, HandleJobActionClickType } from "../../Types/CopyJobTypes";
|
||||
import { getColumns } from "./CopyJobColumns";
|
||||
@@ -26,13 +28,15 @@ interface CopyJobsListProps {
|
||||
}
|
||||
|
||||
const styles = {
|
||||
container: { height: "calc(100vh - 25em)" } as React.CSSProperties,
|
||||
container: { height: "100%" } as React.CSSProperties,
|
||||
stackItem: { position: "relative", marginBottom: "20px" } as React.CSSProperties,
|
||||
};
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
const CopyJobsList: React.FC<CopyJobsListProps> = ({ jobs, handleActionClick, pageSize = PAGE_SIZE }) => {
|
||||
const isDarkMode = useThemeStore((state) => state.isDarkMode);
|
||||
const themeTokens = getThemeTokens(isDarkMode);
|
||||
const [startIndex, setStartIndex] = React.useState(0);
|
||||
const [sortedJobs, setSortedJobs] = React.useState<CopyJobType[]>(jobs);
|
||||
const [sortedColumnKey, setSortedColumnKey] = React.useState<string | undefined>(undefined);
|
||||
@@ -87,11 +91,28 @@ const CopyJobsList: React.FC<CopyJobsListProps> = ({ jobs, handleActionClick, pa
|
||||
enableShimmer={false}
|
||||
constrainMode={ConstrainMode.unconstrained}
|
||||
layoutMode={DetailsListLayoutMode.justified}
|
||||
onRenderDetailsHeader={(props, defaultRender) => (
|
||||
<Sticky stickyPosition={StickyPositionType.Header} isScrollSynced>
|
||||
{defaultRender({ ...props })}
|
||||
</Sticky>
|
||||
)}
|
||||
onRenderDetailsHeader={(props, defaultRender) => {
|
||||
const bgColor = themeTokens.colorNeutralBackground3;
|
||||
const textColor = themeTokens.colorNeutralForeground1;
|
||||
return (
|
||||
<Sticky stickyPosition={StickyPositionType.Header} isScrollSynced stickyBackgroundColor={bgColor}>
|
||||
<div style={{ backgroundColor: bgColor }}>
|
||||
{defaultRender({
|
||||
...props,
|
||||
styles: {
|
||||
root: {
|
||||
backgroundColor: bgColor,
|
||||
selectors: {
|
||||
".ms-DetailsHeader-cellTitle": { color: textColor },
|
||||
".ms-DetailsHeader-cellName": { color: textColor },
|
||||
},
|
||||
},
|
||||
},
|
||||
})}
|
||||
</div>
|
||||
</Sticky>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</ScrollablePane>
|
||||
</Stack.Item>
|
||||
|
||||
@@ -13,7 +13,7 @@ exports[`CopyJobStatusWithIcon Spinner Status Types renders InProgress with spin
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="css-112"
|
||||
class="themeText css-112"
|
||||
>
|
||||
Running
|
||||
</span>
|
||||
@@ -33,7 +33,7 @@ exports[`CopyJobStatusWithIcon Spinner Status Types renders Partitioning with sp
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="css-112"
|
||||
class="themeText css-112"
|
||||
>
|
||||
Running
|
||||
</span>
|
||||
@@ -53,7 +53,7 @@ exports[`CopyJobStatusWithIcon Spinner Status Types renders Running with spinner
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="css-112"
|
||||
class="themeText css-112"
|
||||
>
|
||||
Running
|
||||
</span>
|
||||
@@ -66,7 +66,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
>
|
||||
<i
|
||||
aria-label="Cancelled"
|
||||
class="ms-Icon root-105 css-118 mocked-style-Cancelled"
|
||||
class="ms-Icon root-105 css-118 mocked-styles"
|
||||
data-icon-name="StatusErrorFull"
|
||||
role="img"
|
||||
style="font-family: "FabricMDL2Icons-4";"
|
||||
@@ -74,7 +74,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
|
||||
</i>
|
||||
<span
|
||||
class="css-112"
|
||||
class="themeText css-112"
|
||||
>
|
||||
Cancelled
|
||||
</span>
|
||||
@@ -87,7 +87,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
>
|
||||
<i
|
||||
aria-label="Completed"
|
||||
class="ms-Icon root-105 css-120 mocked-style-Completed"
|
||||
class="ms-Icon root-105 css-120 mocked-styles"
|
||||
data-icon-name="CompletedSolid"
|
||||
role="img"
|
||||
style="font-family: "FabricMDL2Icons-5";"
|
||||
@@ -95,7 +95,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
|
||||
</i>
|
||||
<span
|
||||
class="css-112"
|
||||
class="themeText css-112"
|
||||
>
|
||||
Completed
|
||||
</span>
|
||||
@@ -108,7 +108,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
>
|
||||
<i
|
||||
aria-label="Failed"
|
||||
class="ms-Icon root-105 css-118 mocked-style-Failed"
|
||||
class="ms-Icon root-105 css-118 mocked-styles"
|
||||
data-icon-name="StatusErrorFull"
|
||||
role="img"
|
||||
style="font-family: "FabricMDL2Icons-4";"
|
||||
@@ -116,7 +116,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
|
||||
</i>
|
||||
<span
|
||||
class="css-112"
|
||||
class="themeText css-112"
|
||||
>
|
||||
Failed
|
||||
</span>
|
||||
@@ -129,7 +129,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
>
|
||||
<i
|
||||
aria-label="Faulted"
|
||||
class="ms-Icon root-105 css-118 mocked-style-Faulted"
|
||||
class="ms-Icon root-105 css-118 mocked-styles"
|
||||
data-icon-name="StatusErrorFull"
|
||||
role="img"
|
||||
style="font-family: "FabricMDL2Icons-4";"
|
||||
@@ -137,7 +137,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
|
||||
</i>
|
||||
<span
|
||||
class="css-112"
|
||||
class="themeText css-112"
|
||||
>
|
||||
Failed
|
||||
</span>
|
||||
@@ -150,7 +150,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
>
|
||||
<i
|
||||
aria-label="Paused"
|
||||
class="ms-Icon root-105 css-114 mocked-style-Paused"
|
||||
class="ms-Icon root-105 css-114 mocked-styles"
|
||||
data-icon-name="CirclePause"
|
||||
role="img"
|
||||
style="font-family: "FabricMDL2Icons-11";"
|
||||
@@ -158,7 +158,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
|
||||
</i>
|
||||
<span
|
||||
class="css-112"
|
||||
class="themeText css-112"
|
||||
>
|
||||
Paused
|
||||
</span>
|
||||
@@ -171,7 +171,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
>
|
||||
<i
|
||||
aria-label="Pending"
|
||||
class="ms-Icon root-105 css-111 mocked-style-Pending"
|
||||
class="ms-Icon root-105 css-111 mocked-styles"
|
||||
data-icon-name="Clock"
|
||||
role="img"
|
||||
style="font-family: "FabricMDL2Icons-2";"
|
||||
@@ -179,7 +179,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
|
||||
</i>
|
||||
<span
|
||||
class="css-112"
|
||||
class="themeText css-112"
|
||||
>
|
||||
Queued
|
||||
</span>
|
||||
@@ -192,7 +192,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
>
|
||||
<i
|
||||
aria-label="Skipped"
|
||||
class="ms-Icon root-105 css-116 mocked-style-Skipped"
|
||||
class="ms-Icon root-105 css-116 mocked-styles"
|
||||
data-icon-name="StatusCircleBlock2"
|
||||
role="img"
|
||||
style="font-family: "FabricMDL2Icons-9";"
|
||||
@@ -200,7 +200,7 @@ exports[`CopyJobStatusWithIcon Static Icon Status Types - Snapshot Tests renders
|
||||
|
||||
</i>
|
||||
<span
|
||||
class="css-112"
|
||||
class="themeText css-112"
|
||||
>
|
||||
Cancelled
|
||||
</span>
|
||||
|
||||
@@ -1,6 +1,30 @@
|
||||
@import "../../../less/Common/Constants.less";
|
||||
|
||||
// Common theme-aware classes
|
||||
.themeText {
|
||||
color: var(--colorNeutralForeground1);
|
||||
}
|
||||
|
||||
.themeTextSecondary {
|
||||
color: var(--colorNeutralForeground2);
|
||||
}
|
||||
|
||||
.themeLinkText {
|
||||
color: var(--colorBrandForeground1);
|
||||
}
|
||||
|
||||
.themeBackground {
|
||||
background-color: var(--colorNeutralBackground1);
|
||||
}
|
||||
|
||||
.themeBackgroundSecondary {
|
||||
background-color: var(--colorNeutralBackground2);
|
||||
}
|
||||
|
||||
#containerCopyWrapper {
|
||||
background-color: var(--colorNeutralBackground1);
|
||||
color: var(--colorNeutralForeground1);
|
||||
|
||||
.centerContent {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -9,20 +33,30 @@
|
||||
.noCopyJobsMessage {
|
||||
font-weight: 600;
|
||||
margin: 0 auto;
|
||||
color: @FocusColor;
|
||||
color: var(--colorNeutralForeground2);
|
||||
}
|
||||
button.createCopyJobButton {
|
||||
color: @LinkColor;
|
||||
color: var(--colorBrandForeground1);
|
||||
}
|
||||
}
|
||||
}
|
||||
.createCopyJobScreensContainer {
|
||||
height: 100%;
|
||||
padding: 1em 1.5em;
|
||||
background-color: var(--colorNeutralBackground1);
|
||||
color: var(--colorNeutralForeground1);
|
||||
|
||||
.pointInTimeRestoreContainer, .onlineCopyContainer {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
color: var(--colorNeutralForeground1);
|
||||
}
|
||||
|
||||
.accordionHeaderText {
|
||||
color: var(--colorNeutralForeground1);
|
||||
}
|
||||
|
||||
label {
|
||||
padding: 0;
|
||||
@@ -71,7 +105,7 @@
|
||||
}
|
||||
.foreground {
|
||||
z-index: 10;
|
||||
background-color: #f9f9f9;
|
||||
background-color: var(--colorNeutralBackground2);
|
||||
padding: 20px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
transform: translate(0%, -9%);
|
||||
@@ -83,11 +117,12 @@
|
||||
.create-container-link-btn {
|
||||
padding: 0;
|
||||
height: 25px;
|
||||
color: @LinkColor;
|
||||
color: var(--colorBrandForeground1);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Create collection panel */
|
||||
@@ -105,7 +140,6 @@
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
|
||||
.ms-DetailsList {
|
||||
width: 100%;
|
||||
|
||||
@@ -114,33 +148,33 @@
|
||||
padding: @DefaultSpace 20px;
|
||||
font-weight: 600;
|
||||
font-size: @DefaultFontSize;
|
||||
color: @BaseHigh;
|
||||
background-color: @BaseLow;
|
||||
border-bottom: @ButtonBorderWidth solid @BaseMedium;
|
||||
color: var(--colorNeutralForeground1);
|
||||
background-color: var(--colorNeutralBackground2);
|
||||
border-bottom: @ButtonBorderWidth solid var(--colorNeutralStroke1);
|
||||
|
||||
&:hover {
|
||||
background-color: @BaseMediumLow;
|
||||
background-color: var(--colorNeutralBackground3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ms-DetailsRow {
|
||||
border-bottom: @ButtonBorderWidth solid @BaseMedium;
|
||||
border-bottom: @ButtonBorderWidth solid var(--colorNeutralStroke1);
|
||||
|
||||
&:hover {
|
||||
background-color: @BaseMediumLow;
|
||||
background-color: var(--colorNeutralBackground2);
|
||||
}
|
||||
|
||||
.ms-DetailsRow-cell {
|
||||
padding: @MediumSpace 20px;
|
||||
font-size: @DefaultFontSize;
|
||||
color: @BaseHigh;
|
||||
color: var(--colorNeutralForeground1);
|
||||
min-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.jobNameLink {
|
||||
color: @LinkColor;
|
||||
color: var(--colorBrandForeground1);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@@ -168,7 +202,7 @@
|
||||
}
|
||||
.ms-DetailsRow-cell {
|
||||
font-size: @DefaultFontSize;
|
||||
color: @BaseHigh;
|
||||
color: var(--colorNeutralForeground1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
<Text styles={textSubHeadingStyle}>Current {partitionKeyName.toLowerCase()}</Text>
|
||||
<Text styles={textSubHeadingStyle}>Partitioning</Text>
|
||||
</Stack>
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
<Stack tokens={{ childrenGap: 5 }} data-test="partition-key-values">
|
||||
<Text styles={textSubHeadingStyle1}>{partitionKeyValue}</Text>
|
||||
<Text styles={textSubHeadingStyle1}>
|
||||
{isHierarchicalPartitionedContainer() ? "Hierarchical" : "Non-hierarchical"}
|
||||
@@ -199,6 +199,7 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
{!isReadOnly && (
|
||||
<>
|
||||
<MessageBar
|
||||
data-test="partition-key-warning"
|
||||
messageBarType={MessageBarType.warning}
|
||||
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
||||
styles={darkThemeMessageBarStyles}
|
||||
@@ -220,6 +221,7 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
</Text>
|
||||
{configContext.platform !== Platform.Emulator && (
|
||||
<PrimaryButton
|
||||
data-test="change-partition-key-button"
|
||||
styles={{ root: { width: "fit-content" } }}
|
||||
text="Change"
|
||||
onClick={startPartitionkeyChangeWorkflow}
|
||||
|
||||
@@ -103,7 +103,10 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
|
||||
offText="Inactive"
|
||||
checked={bucket.maxThroughputPercentage !== 100}
|
||||
onChange={(event, checked) => onToggle(bucket.id, checked)}
|
||||
styles={{ root: { marginBottom: 0 }, text: { fontSize: 12 } }}
|
||||
styles={{
|
||||
root: { marginBottom: 0 },
|
||||
text: { fontSize: 12, color: "var(--colorNeutralForeground1)" },
|
||||
}}
|
||||
></Toggle>
|
||||
</Stack>
|
||||
))}
|
||||
|
||||
@@ -78,6 +78,7 @@ exports[`PartitionKeyComponent renders default component and matches snapshot 1`
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack
|
||||
data-test="partition-key-values"
|
||||
tokens={
|
||||
{
|
||||
"childrenGap": 5,
|
||||
@@ -108,6 +109,7 @@ exports[`PartitionKeyComponent renders default component and matches snapshot 1`
|
||||
</Stack>
|
||||
</Stack>
|
||||
<StyledMessageBar
|
||||
data-test="partition-key-warning"
|
||||
messageBarIconProps={
|
||||
{
|
||||
"className": "messageBarWarningIcon",
|
||||
@@ -160,6 +162,7 @@ exports[`PartitionKeyComponent renders default component and matches snapshot 1`
|
||||
To change the partition key, a new destination container must be created or an existing destination container selected. Data will then be copied to the destination container.
|
||||
</Text>
|
||||
<CustomizedPrimaryButton
|
||||
data-test="change-partition-key-button"
|
||||
onClick={[Function]}
|
||||
styles={
|
||||
{
|
||||
@@ -237,6 +240,7 @@ exports[`PartitionKeyComponent renders read-only component and matches snapshot
|
||||
</Text>
|
||||
</Stack>
|
||||
<Stack
|
||||
data-test="partition-key-values"
|
||||
tokens={
|
||||
{
|
||||
"childrenGap": 5,
|
||||
|
||||
@@ -53,6 +53,7 @@ type VectorEmbeddingPolicyProperty = "dataType" | "distanceFunction" | "indexTyp
|
||||
const labelStyles = {
|
||||
root: {
|
||||
fontSize: 12,
|
||||
color: "var(--colorNeutralForeground1)",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -63,6 +64,8 @@ const textFieldStyles: IStyleFunctionOrObject<ITextFieldStyleProps, ITextFieldSt
|
||||
field: {
|
||||
fontSize: 12,
|
||||
padding: "0 8px",
|
||||
backgroundColor: "var(--colorNeutralBackground1)",
|
||||
color: "var(--colorNeutralForeground1)",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -853,7 +853,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
|
||||
{!isSynapseLinkEnabled() && (
|
||||
<Stack className="panelGroupSpacing">
|
||||
<Text variant="small">
|
||||
<Text variant="small" style={{ color: "var(--colorNeutralForeground1)" }}>
|
||||
Azure Synapse Link is required for creating an analytical store{" "}
|
||||
{getCollectionName().toLocaleLowerCase()}. Enable Synapse Link for this Cosmos DB account. <br />
|
||||
<Link
|
||||
|
||||
@@ -475,6 +475,11 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
||||
className="panelGroupSpacing"
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
{
|
||||
"color": "var(--colorNeutralForeground1)",
|
||||
}
|
||||
}
|
||||
variant="small"
|
||||
>
|
||||
Azure Synapse Link is required for creating an analytical store
|
||||
|
||||
@@ -208,7 +208,7 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
|
||||
</div>
|
||||
</Stack>
|
||||
{createNewContainer ? (
|
||||
<Stack>
|
||||
<Stack data-test="create-new-container-form">
|
||||
<MessageBar>All configurations except for unique keys will be copied from the source container</MessageBar>
|
||||
<Stack className="panelGroupSpacing">
|
||||
<Stack horizontal>
|
||||
@@ -230,6 +230,7 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
|
||||
</TooltipHost>
|
||||
</Stack>
|
||||
<input
|
||||
data-test="new-container-id-input"
|
||||
name="collectionId"
|
||||
id="collectionId"
|
||||
type="text"
|
||||
@@ -271,6 +272,7 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
|
||||
|
||||
<input
|
||||
type="text"
|
||||
data-test="new-container-partition-key-input"
|
||||
id="addCollection-partitionKeyValue"
|
||||
aria-required
|
||||
required
|
||||
@@ -304,6 +306,7 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
|
||||
type="text"
|
||||
id="addCollection-partitionKeyValue"
|
||||
key={`addCollection-partitionKeyValue_${index}`}
|
||||
data-test={`new-container-sub-partition-key-input-${index}`}
|
||||
aria-required
|
||||
required
|
||||
size={40}
|
||||
@@ -327,6 +330,8 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
data-test={`remove-sub-partition-key-button-${index}`}
|
||||
ariaLabel="Remove hierarchical partition key"
|
||||
iconProps={{ iconName: "Delete" }}
|
||||
style={{ height: 27 }}
|
||||
onClick={() => {
|
||||
@@ -339,6 +344,7 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
|
||||
})}
|
||||
<Stack className="panelGroupSpacing">
|
||||
<DefaultButton
|
||||
data-test="add-sub-partition-key-button"
|
||||
styles={{ root: { padding: 0, width: 200, height: 30 }, label: { fontSize: 12 } }}
|
||||
disabled={subPartitionKeys.length >= Constants.BackendDefaults.maxNumMultiHashPartition}
|
||||
onClick={() => setSubPartitionKeys([...subPartitionKeys, ""])}
|
||||
@@ -346,7 +352,11 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
|
||||
Add hierarchical partition key
|
||||
</DefaultButton>
|
||||
{subPartitionKeys.length > 0 && (
|
||||
<Text variant="small" style={{ color: "var(--colorNeutralForeground1)" }}>
|
||||
<Text
|
||||
data-test="hierarchical-partitioning-info-text"
|
||||
variant="small"
|
||||
style={{ color: "var(--colorNeutralForeground1)" }}
|
||||
>
|
||||
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={0} /> This feature allows you to
|
||||
partition your data with up to three levels of keys for better data distribution. Requires .NET V3,
|
||||
Java V4 SDK, or preview JavaScript V3 SDK.{" "}
|
||||
@@ -359,7 +369,7 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
|
||||
</Stack>
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack>
|
||||
<Stack data-test="use-existing-container-form">
|
||||
<Stack horizontal>
|
||||
<span className="mandatoryStar">* </span>
|
||||
<Text className="panelTextBold" variant="small">
|
||||
@@ -390,6 +400,7 @@ export const ChangePartitionKeyPane: React.FC<ChangePartitionKeyPaneProps> = ({
|
||||
}}
|
||||
defaultSelectedKey={targetCollectionId}
|
||||
responsiveMode={999}
|
||||
ariaLabel="Existing Containers"
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from "react";
|
||||
import LoadingIndicator_3Squares from "../../../images/LoadingIndicator_3Squares.gif";
|
||||
|
||||
export const PanelLoadingScreen: React.FunctionComponent = () => (
|
||||
<div id="loadingScreen" className="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer">
|
||||
<div id="loadingScreen" className="dataExplorerLoaderContainer dataExplorerLoaderforcopyJobs">
|
||||
<img className="dataExplorerLoader" src={LoadingIndicator_3Squares} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -61,7 +61,8 @@ const useStyles = makeStyles({
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
minHeight: "100vh",
|
||||
height: "100%",
|
||||
overflowY: "auto",
|
||||
backgroundColor: "var(--colorNeutralBackground1)",
|
||||
color: "var(--colorNeutralForeground1)",
|
||||
},
|
||||
@@ -73,20 +74,19 @@ const useStyles = makeStyles({
|
||||
},
|
||||
title: {
|
||||
fontSize: "48px",
|
||||
fontWeight: "500",
|
||||
fontWeight: "400",
|
||||
margin: "16px auto",
|
||||
color: "var(--colorNeutralForeground1)",
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: "18px",
|
||||
marginBottom: "40px",
|
||||
color: "var(--colorNeutralForeground2)",
|
||||
},
|
||||
cardContainer: {
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(2, 1fr)",
|
||||
gap: "16px",
|
||||
width: "66%",
|
||||
width: "60%",
|
||||
margin: "0 auto",
|
||||
backgroundColor: "var(--colorNeutralBackground1)",
|
||||
color: "var(--colorNeutralForeground1)",
|
||||
@@ -100,7 +100,7 @@ const useStyles = makeStyles({
|
||||
color: "var(--colorNeutralForeground1)",
|
||||
border: "1px solid var(--colorNeutralStroke1)",
|
||||
borderRadius: "4px",
|
||||
boxShadow: "var(--shadow4)",
|
||||
boxShadow: "rgba(0, 0, 0, 0.25) 0px 4px 4px",
|
||||
cursor: "pointer",
|
||||
minHeight: "150px",
|
||||
"&:hover": {
|
||||
@@ -128,11 +128,10 @@ const useStyles = makeStyles({
|
||||
textAlign: "left",
|
||||
},
|
||||
moreStuffContainer: {
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(3, 1fr)",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: "32px",
|
||||
width: "66%",
|
||||
margin: "40px auto",
|
||||
width: "90%",
|
||||
},
|
||||
moreStuffColumn: {
|
||||
display: "flex",
|
||||
@@ -227,7 +226,7 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
|
||||
return (
|
||||
<Stack
|
||||
className="splashStackContainer"
|
||||
style={{ width: "66%", cursor: "pointer", margin: "40px auto" }}
|
||||
style={{ width: "60%", cursor: "pointer", margin: "40px auto" }}
|
||||
tokens={{ childrenGap: 16 }}
|
||||
>
|
||||
<Stack className="splashStackRow" horizontal>
|
||||
@@ -903,9 +902,9 @@ export const SplashScreen: React.FC<SplashScreenProps> = ({ explorer }) => {
|
||||
return (
|
||||
<div className={styles.splashScreenContainer}>
|
||||
<div className={styles.splashScreen}>
|
||||
<h1 className={styles.title} role="heading" aria-label="Welcome to Azure Cosmos DB">
|
||||
<h2 className={styles.title} role="heading" aria-label="Welcome to Azure Cosmos DB">
|
||||
Welcome to Azure Cosmos DB<span className="activePatch"></span>
|
||||
</h1>
|
||||
</h2>
|
||||
<div className={styles.subtitle}>Globally distributed, multi-model database service for any scale</div>
|
||||
{getSplashScreenButtons()}
|
||||
{useCarousel.getState().showCoachMark && (
|
||||
|
||||
@@ -15,7 +15,7 @@ const useStyles = makeStyles({
|
||||
button: {
|
||||
border: "1px solid var(--colorNeutralStroke1)",
|
||||
boxSizing: "border-box",
|
||||
boxShadow: "var(--shadow4)",
|
||||
boxShadow: "rgba(0, 0, 0, 0.25) 0px 4px 4px",
|
||||
borderRadius: "4px",
|
||||
padding: "32px 16px",
|
||||
backgroundColor: "var(--colorNeutralBackground1)",
|
||||
|
||||
@@ -125,7 +125,10 @@ const App = (): JSX.Element => {
|
||||
<KeyboardShortcutRoot>
|
||||
<div className="flexContainer" aria-hidden="false">
|
||||
{userContext.features.enableContainerCopy && userContext.apiType === "SQL" ? (
|
||||
<ContainerCopyPanel explorer={explorer} />
|
||||
<>
|
||||
<ContainerCopyPanel explorer={explorer} />
|
||||
<SidePanel />
|
||||
</>
|
||||
) : (
|
||||
<DivExplorer explorer={explorer} />
|
||||
)}
|
||||
|
||||
@@ -438,7 +438,6 @@ body.isDarkMode {
|
||||
|
||||
button {
|
||||
&:not(.ms-Button):not(.ms-IconButton) {
|
||||
background-color: var(--colorNeutralBackground1);
|
||||
color: var(--colorNeutralForeground1);
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -470,6 +470,15 @@ export class DataExplorer {
|
||||
return this.frame.getByTestId("notification-console/header-status");
|
||||
}
|
||||
|
||||
async getDropdownItemByName(name: string, ariaLabel?: string): Promise<Locator> {
|
||||
const dropdownItemsWrapper = this.frame.locator("div.ms-Dropdown-items");
|
||||
if (ariaLabel) {
|
||||
expect(await dropdownItemsWrapper.getAttribute("aria-label")).toEqual(ariaLabel);
|
||||
}
|
||||
const containerDropdownItems = dropdownItemsWrapper.locator("button.ms-Dropdown-item[role='option']");
|
||||
return containerDropdownItems.filter({ hasText: name });
|
||||
}
|
||||
|
||||
/** Waits for the Data Explorer app to load */
|
||||
static async waitForExplorer(page: Page) {
|
||||
const iframeElement = await page.getByTestId("DataExplorerFrame").elementHandle();
|
||||
|
||||
@@ -9,7 +9,7 @@ let queryTab: QueryTab = null!;
|
||||
let queryEditor: Editor = null!;
|
||||
|
||||
test.beforeAll("Create Test Database", async () => {
|
||||
context = await createTestSQLContainer(true);
|
||||
context = await createTestSQLContainer({ includeTestData: true });
|
||||
});
|
||||
|
||||
test.beforeEach("Open new query tab", async ({ page }) => {
|
||||
|
||||
98
test/sql/scaleAndSettings/changePartitionKey.spec.ts
Normal file
98
test/sql/scaleAndSettings/changePartitionKey.spec.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { expect, Page, test } from "@playwright/test";
|
||||
import { DataExplorer, TestAccount } from "../../fx";
|
||||
import { createTestSQLContainer, TestContainerContext } from "../../testData";
|
||||
|
||||
test.describe("Change Partition Key", () => {
|
||||
let pageInstance: Page;
|
||||
let context: TestContainerContext = null!;
|
||||
let explorer: DataExplorer = null!;
|
||||
const newPartitionKeyPath = "/newPartitionKey";
|
||||
const newContainerId = "testcontainer_1";
|
||||
|
||||
test.beforeAll("Create Test Database", async () => {
|
||||
context = await createTestSQLContainer();
|
||||
});
|
||||
|
||||
test.beforeEach("Open container settings", async ({ page }) => {
|
||||
pageInstance = page;
|
||||
explorer = await DataExplorer.open(page, TestAccount.SQL);
|
||||
|
||||
// Click Scale & Settings and open Partition Key tab
|
||||
await explorer.openScaleAndSettings(context);
|
||||
const PartitionKeyTab = explorer.frame.getByTestId("settings-tab-header/PartitionKeyTab");
|
||||
await PartitionKeyTab.click();
|
||||
});
|
||||
|
||||
test.afterAll("Delete Test Database", async () => {
|
||||
await context?.dispose();
|
||||
});
|
||||
|
||||
test("Change partition key path", async () => {
|
||||
await expect(explorer.frame.getByText("/partitionKey")).toBeVisible();
|
||||
await expect(explorer.frame.getByText("Change partition key")).toBeVisible();
|
||||
await expect(explorer.frame.getByText(/To safeguard the integrity of/)).toBeVisible();
|
||||
await expect(explorer.frame.getByText(/To change the partition key/)).toBeVisible();
|
||||
|
||||
const changePartitionKeyButton = explorer.frame.getByTestId("change-partition-key-button");
|
||||
expect(changePartitionKeyButton).toBeVisible();
|
||||
await changePartitionKeyButton.click();
|
||||
|
||||
// Fill out new partition key form in the panel
|
||||
const changePkPanel = explorer.frame.getByTestId(`Panel:Change partition key`);
|
||||
await expect(changePkPanel.getByText(context.database.id)).toBeVisible();
|
||||
await expect(explorer.frame.getByRole("heading", { name: "Change partition key" })).toBeVisible();
|
||||
await expect(explorer.frame.getByText(/When changing a container/)).toBeVisible();
|
||||
|
||||
// Try to switch to new container
|
||||
await expect(changePkPanel.getByText("New container")).toBeVisible();
|
||||
await expect(changePkPanel.getByText("Existing container")).toBeVisible();
|
||||
await expect(changePkPanel.getByTestId("new-container-id-input")).toBeVisible();
|
||||
|
||||
changePkPanel.getByTestId("new-container-id-input").fill(newContainerId);
|
||||
await expect(changePkPanel.getByTestId("new-container-partition-key-input")).toBeVisible();
|
||||
changePkPanel.getByTestId("new-container-partition-key-input").fill(newPartitionKeyPath);
|
||||
|
||||
await expect(changePkPanel.getByTestId("add-sub-partition-key-button")).toBeVisible();
|
||||
changePkPanel.getByTestId("add-sub-partition-key-button").click();
|
||||
await expect(changePkPanel.getByTestId("new-container-sub-partition-key-input-0")).toBeVisible();
|
||||
await expect(changePkPanel.getByTestId("remove-sub-partition-key-button-0")).toBeVisible();
|
||||
await expect(changePkPanel.getByTestId("hierarchical-partitioning-info-text")).toBeVisible();
|
||||
changePkPanel.getByTestId("new-container-sub-partition-key-input-0").fill("/customerId");
|
||||
|
||||
await changePkPanel.getByTestId("Panel/OkButton").click();
|
||||
|
||||
await pageInstance.waitForLoadState("networkidle");
|
||||
await expect(changePkPanel).not.toBeVisible({ timeout: 60 * 1000 });
|
||||
|
||||
// Verify partition key change job
|
||||
const jobText = explorer.frame.getByText(/Partition key change job/);
|
||||
await expect(jobText).toBeVisible();
|
||||
await expect(explorer.frame.locator(".ms-ProgressIndicator-itemName")).toContainText("Portal_testcontainer_1");
|
||||
|
||||
const jobRow = explorer.frame.locator(".ms-ProgressIndicator-itemDescription");
|
||||
await expect(jobRow.getByText("Completed")).toBeVisible({ timeout: 30 * 1000 });
|
||||
|
||||
const newContainerNode = await explorer.waitForContainerNode(context.database.id, newContainerId);
|
||||
expect(newContainerNode).not.toBeNull();
|
||||
|
||||
// Now try to switch to existing container
|
||||
await changePartitionKeyButton.click();
|
||||
await changePkPanel.getByText("Existing container").click();
|
||||
await changePkPanel.getByLabel("Use existing container").check();
|
||||
await changePkPanel.getByText("Choose an existing container").click();
|
||||
|
||||
const containerDropdownItem = await explorer.getDropdownItemByName(newContainerId, "Existing Containers");
|
||||
await containerDropdownItem.click();
|
||||
|
||||
await changePkPanel.getByTestId("Panel/OkButton").click();
|
||||
await explorer.frame.getByRole("button", { name: "Cancel" }).click();
|
||||
|
||||
// Dismiss overlay if it appears
|
||||
const overlayFrame = explorer.frame.locator("#webpack-dev-server-client-overlay").first();
|
||||
if (await overlayFrame.count()) {
|
||||
await overlayFrame.contentFrame().getByLabel("Dismiss").click();
|
||||
}
|
||||
const cancelledJobRow = explorer.frame.getByTestId("Tab:tab0");
|
||||
await expect(cancelledJobRow.getByText("Cancelled")).toBeVisible({ timeout: 30 * 1000 });
|
||||
});
|
||||
});
|
||||
@@ -14,7 +14,7 @@ test.describe("Autoscale and Manual throughput", () => {
|
||||
let explorer: DataExplorer = null!;
|
||||
|
||||
test.beforeAll("Create Test Database", async () => {
|
||||
context = await createTestSQLContainer(true);
|
||||
context = await createTestSQLContainer({ includeTestData: true });
|
||||
});
|
||||
|
||||
test.beforeEach("Open container settings", async ({ page }) => {
|
||||
|
||||
@@ -7,7 +7,7 @@ test.describe("Settings under Scale & Settings", () => {
|
||||
let explorer: DataExplorer = null!;
|
||||
|
||||
test.beforeAll("Create Test Database", async () => {
|
||||
context = await createTestSQLContainer(true);
|
||||
context = await createTestSQLContainer({ includeTestData: true });
|
||||
});
|
||||
|
||||
test.beforeEach("Open Settings tab under Scale & Settings", async ({ page }) => {
|
||||
|
||||
@@ -74,8 +74,18 @@ export class TestContainerContext {
|
||||
}
|
||||
}
|
||||
|
||||
export async function createTestSQLContainer(includeTestData?: boolean) {
|
||||
const databaseId = generateUniqueName("db");
|
||||
type createTestSqlContainerConfig = {
|
||||
includeTestData?: boolean;
|
||||
partitionKey?: string;
|
||||
databaseName?: string;
|
||||
};
|
||||
|
||||
export async function createTestSQLContainer({
|
||||
includeTestData = false,
|
||||
partitionKey = "/partitionKey",
|
||||
databaseName = "",
|
||||
}: createTestSqlContainerConfig = {}) {
|
||||
const databaseId = databaseName ? databaseName : generateUniqueName("db");
|
||||
const containerId = "testcontainer"; // A unique container name isn't needed because the database is unique
|
||||
const credentials = getAzureCLICredentials();
|
||||
const adaptedCredentials = new AzureIdentityCredentialAdapter(credentials);
|
||||
@@ -104,7 +114,7 @@ export async function createTestSQLContainer(includeTestData?: boolean) {
|
||||
try {
|
||||
const { container } = await database.containers.createIfNotExists({
|
||||
id: containerId,
|
||||
partitionKey: "/partitionKey",
|
||||
partitionKey,
|
||||
});
|
||||
if (includeTestData) {
|
||||
const batchCount = TestData.length / 100;
|
||||
|
||||
Reference in New Issue
Block a user