[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:
v-darkora 2023-07-07 09:09:15 +02:00 committed by GitHub
parent 3bef1ed136
commit 0eaa5d004b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 55 additions and 17 deletions

View File

@ -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 => {

View File

@ -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}
/> />
); );
}; };

View File

@ -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"

View File

@ -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 />}

View File

@ -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>

View File

@ -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"

View File

@ -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 }),
})); }));