mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-07-06 20:13:47 +01:00
[Query Copilot] Pin panel footer to the bottom, remove gap between panel and console (#1511)
* Move footer when save button is enabled to the bottom, remove gap between notification console and right panel * Change the way panel height is calculated * Remove unnecessary operator * Change condition * Fix snapshot * Update panel height after animation ends and use different css for showing save button to the bottom of the page * Fix ts compile
This commit is contained in:
parent
3bef1ed136
commit
0eaa5d004b
@ -5,18 +5,18 @@
|
|||||||
import { Dropdown, IDropdownOption } from "@fluentui/react";
|
import { Dropdown, IDropdownOption } from "@fluentui/react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import AnimateHeight from "react-animate-height";
|
import AnimateHeight from "react-animate-height";
|
||||||
import LoaderIcon from "../../../../images/circular_loader_black_16x16.gif";
|
|
||||||
import ClearIcon from "../../../../images/Clear-1.svg";
|
import ClearIcon from "../../../../images/Clear-1.svg";
|
||||||
|
import ChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
|
||||||
|
import ChevronUpIcon from "../../../../images/QueryBuilder/CollapseChevronUp_16x.png";
|
||||||
|
import LoaderIcon from "../../../../images/circular_loader_black_16x16.gif";
|
||||||
import ErrorBlackIcon from "../../../../images/error_black.svg";
|
import ErrorBlackIcon from "../../../../images/error_black.svg";
|
||||||
import ErrorRedIcon from "../../../../images/error_red.svg";
|
import ErrorRedIcon from "../../../../images/error_red.svg";
|
||||||
import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg";
|
import infoBubbleIcon from "../../../../images/info-bubble-9x9.svg";
|
||||||
import InfoIcon from "../../../../images/info_color.svg";
|
import InfoIcon from "../../../../images/info_color.svg";
|
||||||
import LoadingIcon from "../../../../images/loading.svg";
|
import LoadingIcon from "../../../../images/loading.svg";
|
||||||
import ChevronDownIcon from "../../../../images/QueryBuilder/CollapseChevronDown_16x.png";
|
|
||||||
import ChevronUpIcon from "../../../../images/QueryBuilder/CollapseChevronUp_16x.png";
|
|
||||||
import { ClientDefaults, KeyCodes } from "../../../Common/Constants";
|
import { ClientDefaults, KeyCodes } from "../../../Common/Constants";
|
||||||
import { useNotificationConsole } from "../../../hooks/useNotificationConsole";
|
|
||||||
import { userContext } from "../../../UserContext";
|
import { userContext } from "../../../UserContext";
|
||||||
|
import { useNotificationConsole } from "../../../hooks/useNotificationConsole";
|
||||||
import { ConsoleData, ConsoleDataType } from "./ConsoleData";
|
import { ConsoleData, ConsoleDataType } from "./ConsoleData";
|
||||||
|
|
||||||
export interface NotificationConsoleComponentProps {
|
export interface NotificationConsoleComponentProps {
|
||||||
@ -256,6 +256,7 @@ export class NotificationConsoleComponent extends React.Component<
|
|||||||
if (this.props.isConsoleExpanded && this.consoleHeaderElement) {
|
if (this.props.isConsoleExpanded && this.consoleHeaderElement) {
|
||||||
this.consoleHeaderElement.focus();
|
this.consoleHeaderElement.focus();
|
||||||
}
|
}
|
||||||
|
useNotificationConsole.getState().setConsoleAnimationFinished(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
private updateConsoleData = (prevProps: NotificationConsoleComponentProps): void => {
|
private updateConsoleData = (prevProps: NotificationConsoleComponentProps): void => {
|
||||||
|
@ -8,6 +8,7 @@ export interface PanelContainerProps {
|
|||||||
panelContent?: JSX.Element;
|
panelContent?: JSX.Element;
|
||||||
isConsoleExpanded: boolean;
|
isConsoleExpanded: boolean;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
|
isConsoleAnimationFinished?: boolean;
|
||||||
panelWidth?: string;
|
panelWidth?: string;
|
||||||
onRenderNavigationContent?: IRenderFunction<IPanelProps>;
|
onRenderNavigationContent?: IRenderFunction<IPanelProps>;
|
||||||
}
|
}
|
||||||
@ -28,7 +29,7 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
omponentDidMount(): void {
|
||||||
window.addEventListener("resize", () => this.setState({ height: this.getPanelHeight() }));
|
window.addEventListener("resize", () => this.setState({ height: this.getPanelHeight() }));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +37,15 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
|
|||||||
window.removeEventListener("resize", () => this.setState({ height: this.getPanelHeight() }));
|
window.removeEventListener("resize", () => this.setState({ height: this.getPanelHeight() }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(): void {
|
||||||
|
if (useNotificationConsole.getState().consoleAnimationFinished || this.state.height !== this.getPanelHeight()) {
|
||||||
|
this.setState({
|
||||||
|
height: this.getPanelHeight(),
|
||||||
|
});
|
||||||
|
useNotificationConsole.getState().setConsoleAnimationFinished(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
if (!this.props.panelContent) {
|
if (!this.props.panelContent) {
|
||||||
return <></>;
|
return <></>;
|
||||||
@ -59,7 +69,7 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
|
|||||||
header: { padding: "0 0 8px 34px" },
|
header: { padding: "0 0 8px 34px" },
|
||||||
commands: { marginTop: 8 },
|
commands: { marginTop: 8 },
|
||||||
}}
|
}}
|
||||||
style={{ height: this.getPanelHeight() }}
|
style={{ height: this.state.height }}
|
||||||
>
|
>
|
||||||
{this.props.panelContent}
|
{this.props.panelContent}
|
||||||
</Panel>
|
</Panel>
|
||||||
@ -75,16 +85,22 @@ export class PanelContainerComponent extends React.Component<PanelContainerProps
|
|||||||
};
|
};
|
||||||
|
|
||||||
private getPanelHeight = (): string => {
|
private getPanelHeight = (): string => {
|
||||||
const consoleHeight = this.props.isConsoleExpanded
|
const notificationConsole = document.getElementById("explorerNotificationConsole");
|
||||||
? PanelContainerComponent.consoleContentHeight + PanelContainerComponent.consoleHeaderHeight
|
if (notificationConsole) {
|
||||||
: PanelContainerComponent.consoleHeaderHeight;
|
return window.innerHeight - notificationConsole.clientHeight + "px";
|
||||||
const panelHeight = window.innerHeight - consoleHeight;
|
}
|
||||||
return panelHeight + "px";
|
return (
|
||||||
|
window.innerHeight -
|
||||||
|
(this.props.isConsoleExpanded
|
||||||
|
? PanelContainerComponent.consoleContentHeight + PanelContainerComponent.consoleHeaderHeight
|
||||||
|
: PanelContainerComponent.consoleHeaderHeight) +
|
||||||
|
"px"
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SidePanel: React.FC = () => {
|
export const SidePanel: React.FC = () => {
|
||||||
const isConsoleExpanded = useNotificationConsole((state) => state.isExpanded);
|
const isConsoleExpanded = useNotificationConsole((state) => state.isExpanded);
|
||||||
|
const isConsoleAnimationFinished = useNotificationConsole((state) => state.consoleAnimationFinished);
|
||||||
const { isOpen, panelContent, panelWidth, headerText } = useSidePanel((state) => {
|
const { isOpen, panelContent, panelWidth, headerText } = useSidePanel((state) => {
|
||||||
return {
|
return {
|
||||||
isOpen: state.isOpen,
|
isOpen: state.isOpen,
|
||||||
@ -102,6 +118,7 @@ export const SidePanel: React.FC = () => {
|
|||||||
headerText={headerText}
|
headerText={headerText}
|
||||||
isConsoleExpanded={isConsoleExpanded}
|
isConsoleExpanded={isConsoleExpanded}
|
||||||
panelWidth={panelWidth}
|
panelWidth={panelWidth}
|
||||||
|
isConsoleAnimationFinished={isConsoleAnimationFinished}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
import { PrimaryButton } from "@fluentui/react";
|
import { PrimaryButton } from "@fluentui/react";
|
||||||
import React from "react";
|
import React, { CSSProperties } from "react";
|
||||||
|
|
||||||
export interface PanelFooterProps {
|
export interface PanelFooterProps {
|
||||||
buttonLabel: string;
|
buttonLabel: string;
|
||||||
isButtonDisabled?: boolean;
|
isButtonDisabled?: boolean;
|
||||||
|
style?: CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PanelFooterComponent: React.FunctionComponent<PanelFooterProps> = ({
|
export const PanelFooterComponent: React.FunctionComponent<PanelFooterProps> = ({
|
||||||
buttonLabel,
|
buttonLabel,
|
||||||
isButtonDisabled,
|
isButtonDisabled,
|
||||||
|
style,
|
||||||
}: PanelFooterProps): JSX.Element => (
|
}: PanelFooterProps): JSX.Element => (
|
||||||
<div className="panelFooter">
|
<div className="panelFooter" style={style}>
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
type="submit"
|
type="submit"
|
||||||
id="sidePanelOkButton"
|
id="sidePanelOkButton"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { FunctionComponent, ReactNode } from "react";
|
import React, { CSSProperties, FunctionComponent, ReactNode } from "react";
|
||||||
import { PanelFooterComponent } from "../PanelFooterComponent";
|
import { PanelFooterComponent } from "../PanelFooterComponent";
|
||||||
import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent";
|
import { PanelInfoErrorComponent } from "../PanelInfoErrorComponent";
|
||||||
import { PanelLoadingScreen } from "../PanelLoadingScreen";
|
import { PanelLoadingScreen } from "../PanelLoadingScreen";
|
||||||
@ -11,6 +11,7 @@ export interface RightPaneFormProps {
|
|||||||
isSubmitButtonHidden?: boolean;
|
isSubmitButtonHidden?: boolean;
|
||||||
isSubmitButtonDisabled?: boolean;
|
isSubmitButtonDisabled?: boolean;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
|
footerStyle?: CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RightPaneForm: FunctionComponent<RightPaneFormProps> = ({
|
export const RightPaneForm: FunctionComponent<RightPaneFormProps> = ({
|
||||||
@ -21,6 +22,7 @@ export const RightPaneForm: FunctionComponent<RightPaneFormProps> = ({
|
|||||||
isSubmitButtonHidden = false,
|
isSubmitButtonHidden = false,
|
||||||
isSubmitButtonDisabled = false,
|
isSubmitButtonDisabled = false,
|
||||||
children,
|
children,
|
||||||
|
footerStyle,
|
||||||
}: RightPaneFormProps) => {
|
}: RightPaneFormProps) => {
|
||||||
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
const handleOnSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -33,7 +35,11 @@ export const RightPaneForm: FunctionComponent<RightPaneFormProps> = ({
|
|||||||
{formError && <PanelInfoErrorComponent messageType="error" message={formError} showErrorDetails={true} />}
|
{formError && <PanelInfoErrorComponent messageType="error" message={formError} showErrorDetails={true} />}
|
||||||
{children}
|
{children}
|
||||||
{!isSubmitButtonHidden && (
|
{!isSubmitButtonHidden && (
|
||||||
<PanelFooterComponent buttonLabel={submitButtonText} isButtonDisabled={isSubmitButtonDisabled} />
|
<PanelFooterComponent
|
||||||
|
buttonLabel={submitButtonText}
|
||||||
|
isButtonDisabled={isSubmitButtonDisabled}
|
||||||
|
style={footerStyle}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</form>
|
</form>
|
||||||
{isExecuting && <PanelLoadingScreen />}
|
{isExecuting && <PanelLoadingScreen />}
|
||||||
|
@ -139,10 +139,11 @@ export const SaveQueryPane: FunctionComponent<SaveQueryPaneProps> = ({
|
|||||||
onSubmit: () => {
|
onSubmit: () => {
|
||||||
isSaveQueryEnabled() ? submit() : setupQueries();
|
isSaveQueryEnabled() ? submit() : setupQueries();
|
||||||
},
|
},
|
||||||
|
footerStyle: isSaveQueryEnabled() ? { flexGrow: 0 } : {},
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<RightPaneForm {...props}>
|
<RightPaneForm {...props}>
|
||||||
<div className="panelFormWrapper">
|
<div className="panelFormWrapper" style={{ flexGrow: 1 }}>
|
||||||
<div className="panelMainContent">
|
<div className="panelMainContent">
|
||||||
{!isSaveQueryEnabled() ? (
|
{!isSaveQueryEnabled() ? (
|
||||||
<Text variant="small">{setupSaveQueriesText}</Text>
|
<Text variant="small">{setupSaveQueriesText}</Text>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
exports[`Save Query Pane should render Default properly 1`] = `
|
exports[`Save Query Pane should render Default properly 1`] = `
|
||||||
<RightPaneForm
|
<RightPaneForm
|
||||||
|
footerStyle={Object {}}
|
||||||
formError=""
|
formError=""
|
||||||
isExecuting={false}
|
isExecuting={false}
|
||||||
onSubmit={[Function]}
|
onSubmit={[Function]}
|
||||||
@ -9,6 +10,11 @@ exports[`Save Query Pane should render Default properly 1`] = `
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="panelFormWrapper"
|
className="panelFormWrapper"
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"flexGrow": 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="panelMainContent"
|
className="panelMainContent"
|
||||||
|
@ -5,21 +5,26 @@ export interface NotificationConsoleState {
|
|||||||
isExpanded: boolean;
|
isExpanded: boolean;
|
||||||
inProgressConsoleDataIdToBeDeleted: string;
|
inProgressConsoleDataIdToBeDeleted: string;
|
||||||
consoleData: ConsoleData | undefined;
|
consoleData: ConsoleData | undefined;
|
||||||
|
consoleAnimationFinished: boolean;
|
||||||
expandConsole: () => void;
|
expandConsole: () => void;
|
||||||
// TODO Remove this method. Add a `closeConsole` method instead
|
// TODO Remove this method. Add a `closeConsole` method instead
|
||||||
setIsExpanded: (isExpanded: boolean) => void;
|
setIsExpanded: (isExpanded: boolean) => void;
|
||||||
// TODO These two methods badly need a refactor. Not very react friendly.
|
// TODO These two methods badly need a refactor. Not very react friendly.
|
||||||
setNotificationConsoleData: (consoleData: ConsoleData) => void;
|
setNotificationConsoleData: (consoleData: ConsoleData) => void;
|
||||||
setInProgressConsoleDataIdToBeDeleted: (id: string) => void;
|
setInProgressConsoleDataIdToBeDeleted: (id: string) => void;
|
||||||
|
setConsoleAnimationFinished: (consoleAnimationFinished: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useNotificationConsole: UseStore<NotificationConsoleState> = create((set) => ({
|
export const useNotificationConsole: UseStore<NotificationConsoleState> = create((set) => ({
|
||||||
isExpanded: false,
|
isExpanded: false,
|
||||||
consoleData: undefined,
|
consoleData: undefined,
|
||||||
inProgressConsoleDataIdToBeDeleted: "",
|
inProgressConsoleDataIdToBeDeleted: "",
|
||||||
|
consoleAnimationFinished: false,
|
||||||
expandConsole: () => set((state) => ({ ...state, isExpanded: true })),
|
expandConsole: () => set((state) => ({ ...state, isExpanded: true })),
|
||||||
setIsExpanded: (isExpanded) => set((state) => ({ ...state, isExpanded })),
|
setIsExpanded: (isExpanded) => set((state) => ({ ...state, isExpanded })),
|
||||||
setNotificationConsoleData: (consoleData: ConsoleData) => set((state) => ({ ...state, consoleData })),
|
setNotificationConsoleData: (consoleData: ConsoleData) => set((state) => ({ ...state, consoleData })),
|
||||||
setInProgressConsoleDataIdToBeDeleted: (id: string) =>
|
setInProgressConsoleDataIdToBeDeleted: (id: string) =>
|
||||||
set((state) => ({ ...state, inProgressConsoleDataIdToBeDeleted: id })),
|
set((state) => ({ ...state, inProgressConsoleDataIdToBeDeleted: id })),
|
||||||
|
setConsoleAnimationFinished: (consoleAnimationFinished: boolean) =>
|
||||||
|
set({ consoleAnimationFinished: consoleAnimationFinished }),
|
||||||
}));
|
}));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user