Fixed a button focus issue found while testing the dialog button changes. #6513

This commit is contained in:
Rohit Bhati 2025-01-08 17:22:11 +05:30 committed by GitHub
parent 172edaa227
commit de6fbe7725
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 93 additions and 82 deletions

View File

@ -271,7 +271,8 @@ define('pgadmin.node.database', [
},
function() { return true;},
gettext('Disconnect'),
gettext('Cancel')
gettext('Cancel'),
'disconnect'
);
}
@ -565,7 +566,8 @@ define('pgadmin.node.database', [
},
function() { return true; },
gettext('Disconnect'),
gettext('Cancel')
gettext('Cancel'),
'disconnect'
);
} else {
disconnect();

View File

@ -261,6 +261,7 @@ define('pgadmin.node.server', [
function() { return true;},
gettext('Disconnect'),
gettext('Cancel'),
'disconnect'
);
}
return false;
@ -868,7 +869,8 @@ define('pgadmin.node.server', [
function() { disconnect(); },
function() { return true;},
gettext('Disconnect'),
gettext('Cancel')
gettext('Cancel'),
'disconnect'
);
} else {
disconnect();

View File

@ -15,6 +15,8 @@ import React, { useEffect, useMemo } from 'react';
import { FileType } from 'react-aspen';
import { Box } from '@mui/material';
import PropTypes from 'prop-types';
import CloseIcon from '@mui/icons-material/CloseRounded';
import HTMLReactParser from 'html-react-parser/lib/index';
import SchemaView from '../../../../static/js/SchemaView';
import getApiInstance from '../../../../static/js/api_instance';
import CloseSharpIcon from '@mui/icons-material/CloseSharp';
@ -85,6 +87,16 @@ const StyledBox = styled(Box)(({theme}) => ({
marginLeft: '0.5em'
},
},
},
'& .Alert-footer': {
display: 'flex',
justifyContent: 'flex-end',
padding: '0.5rem',
...theme.mixins.panelBorder.top,
},
'& .Alert-margin': {
marginLeft: '0.25rem',
},
}));
@ -655,36 +667,31 @@ export default function PreferencesComponent({ ...props }) {
};
const reset = () => {
pgAdmin.Browser.notifier.confirm(
const text = `${gettext('All preferences will be reset to their default values.')}<br><br>${gettext('Do you want to proceed?')}<br><br>
${gettext('Note:')}<br> <ul style="padding-left:20px"><li style="list-style-type:disc">${gettext('The object explorer tree will be refreshed automatically to reflect the changes.')}</li>
<li style="list-style-type:disc">${gettext('If the application language changes, a reload of the application will be required. You can choose to reload later at your convenience.')}</li></ul>`;
pgAdmin.Browser.notifier.showModal(
gettext('Reset all preferences'),
`${gettext('All preferences will be reset to their default values.')}<br><br>${gettext('Do you want to proceed?')}<br><br>
${gettext('Note:')}<br> <ul style="padding-left:20px"><li style="list-style-type:disc">${gettext('The object explorer tree will be refreshed automatically to reflect the changes.')}</li>
<li style="list-style-type:disc">${gettext('If the application language changes, a reload of the application will be required. You can choose to reload later at your convenience.')}</li></ul>`,
function () {},
function () {},
'',
'Cancel',
function (closeModal) {
return [
{
type: 'default',
icon: <SaveSharpIcon />,
label: gettext('Save & Reload'),
onclick: () => {
resetPrefsToDefault(true);
closeModal();
}
}, {
type: 'primary',
icon: <SaveSharpIcon />,
label: gettext('Save & Reload Later'),
onclick: () => {
resetPrefsToDefault(false);
closeModal();
}
}
];
}
(closeModal)=>{
const onClick = (reset) => {
resetPrefsToDefault(reset);
closeModal();
};
return(
<StyledBox display="flex" flexDirection="column" height="100%">
<Box flexGrow="1" p={2}>
{HTMLReactParser(text)}
</Box>
<Box className='Alert-footer'>
<DefaultButton className='Alert-margin' startIcon={<CloseIcon />} onClick={()=> closeModal()}>{'Cancel'}</DefaultButton>
<DefaultButton className='Alert-margin' startIcon={<SaveSharpIcon />} onClick={() => onClick(true)} >{gettext('Save & Reload')}</DefaultButton>
<PrimaryButton className='Alert-margin' startIcon={ <SaveSharpIcon />} onClick={()=>onClick(false)}>{gettext('Save & Reload Later')}</PrimaryButton>
</Box>
</StyledBox>
);
},
{ isFullScreen: false, isResizeable: false, showFullScreen: false, isFullWidth: false, showTitle: true},
);
};

View File

@ -71,8 +71,8 @@ ClearIcon.propTypes = {style: PropTypes.object};
export const ConnectedIcon = ({style})=><ExternalIcon Icon={ConnectedSvg} style={{height: '1rem', ...style}} data-label="ConnectedIcon" />;
ConnectedIcon.propTypes = {style: PropTypes.object};
export const DisonnectedIcon = ({style})=><ExternalIcon Icon={DisconnectedSvg} style={{height: '1rem', ...style}} data-label="DisonnectedIcon" />;
DisonnectedIcon.propTypes = {style: PropTypes.object};
export const DisconnectedIcon = ({style})=><ExternalIcon Icon={DisconnectedSvg} style={{height: '1rem', ...style}} data-label="DisconnectedIcon" />;
DisconnectedIcon.propTypes = {style: PropTypes.object};
export const RegexIcon = ({style})=><ExternalIcon Icon={RegexSvg} style={style} data-label="RegexIcon" />;
RegexIcon.propTypes = {style: PropTypes.object};

View File

@ -13,13 +13,14 @@ import { getEpoch } from 'sources/utils';
import { DefaultButton, PgIconButton, PrimaryButton } from '../components/Buttons';
import Draggable from 'react-draggable';
import CloseIcon from '@mui/icons-material/CloseRounded';
import DeleteIcon from '@mui/icons-material/Delete';
import CustomPropTypes from '../custom_prop_types';
import PropTypes from 'prop-types';
import gettext from 'sources/gettext';
import HTMLReactParser from 'html-react-parser';
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
import { Rnd } from 'react-rnd';
import { ExpandDialogIcon, MinimizeDialogIcon } from '../components/ExternalIcon';
import { ExpandDialogIcon, MinimizeDialogIcon, DisconnectedIcon } from '../components/ExternalIcon';
import { styled } from '@mui/material/styles';
export const ModalContext = React.createContext({});
@ -37,35 +38,24 @@ const StyledBox = styled(Box)(({theme}) => ({
},
}));
const buttonIconMap = {
disconnect: <DisconnectedIcon />,
default: <CheckRoundedIcon />
};
export function useModal() {
return React.useContext(ModalContext);
}
function renderExtraButtons(button) {
switch(button.type) {
case 'primary':
return <PrimaryButton className='Alert-margin' startIcon={button.icon} onClick={button.onClick}>{button.label}</PrimaryButton>;
case 'default':
return <DefaultButton className='Alert-margin' startIcon={button.icon} onClick={button.onClick} color={button?.color}>{button.label}</DefaultButton>;
default:
return <DefaultButton className='Alert-margin' startIcon={button.icon} onClick={button.onClick}>{button.label}</DefaultButton>;
};
}
function AlertContent({ text, confirm, okLabel = gettext('OK'), cancelLabel = gettext('Cancel'), onOkClick, onCancelClick, extraButtons }) {
function AlertContent({ text, confirm, okLabel = gettext('OK'), cancelLabel = gettext('Cancel'), onOkClick, onCancelClick, okIcon = 'default'}) {
return (
<StyledBox display="flex" flexDirection="column" height="100%">
<Box flexGrow="1" p={2}>{typeof (text) == 'string' ? HTMLReactParser(text) : text}</Box>
<Box className='Alert-footer'>
{confirm &&
<DefaultButton startIcon={<CloseIcon />} onClick={onCancelClick} autoFocus={true}>{cancelLabel}</DefaultButton>
}
{
extraButtons?.length ?
extraButtons.map(button=>renderExtraButtons(button))
:
<PrimaryButton className='Alert-margin' startIcon={<CheckRoundedIcon />} onClick={onOkClick}>{okLabel}</PrimaryButton>
<DefaultButton startIcon={<CloseIcon />} onClick={onCancelClick}>{cancelLabel}</DefaultButton>
}
<PrimaryButton className='Alert-margin' startIcon={buttonIconMap[okIcon]} onClick={onOkClick} autoFocus>{okLabel}</PrimaryButton>
</Box>
</StyledBox>
);
@ -77,7 +67,7 @@ AlertContent.propTypes = {
onCancelClick: PropTypes.func,
okLabel: PropTypes.string,
cancelLabel: PropTypes.string,
extraButtons: PropTypes.array
okIcon : PropTypes.string
};
function alert(title, text, onOkClick, okLabel = gettext('OK')) {
@ -93,7 +83,7 @@ function alert(title, text, onOkClick, okLabel = gettext('OK')) {
});
}
function confirm(title, text, onOkClick, onCancelClick, okLabel = gettext('Yes'), cancelLabel = gettext('No'), extras = null) {
function confirm(title, text, onOkClick, onCancelClick, okLabel = gettext('Yes'), cancelLabel = gettext('No'), okIcon) {
// bind the modal provider before calling
this.showModal(title, (closeModal) => {
const onCancelClickClose = () => {
@ -105,12 +95,36 @@ function confirm(title, text, onOkClick, onCancelClick, okLabel = gettext('Yes')
onOkClick?.();
closeModal();
};
const extraButtons = extras?.(closeModal);
return (
<AlertContent text={text} confirm onOkClick={onOkClickClose} onCancelClick={onCancelClickClose} okLabel={okLabel} cancelLabel={cancelLabel} extraButtons={extraButtons} />
<AlertContent text={text} confirm onOkClick={onOkClickClose} onCancelClick={onCancelClickClose} okLabel={okLabel} cancelLabel={cancelLabel} okIcon={okIcon}/>
);
});
}
function confirmDelete(title, text, onDeleteClick, onCancelClick, deleteLabel = gettext('Delete'), cancelLabel = gettext('Cancel')) {
this.showModal(
title,
(closeModal)=>{
const handleOkClose = (callback) => {
callback();
closeModal();
};
return (
<StyledBox display="flex" flexDirection="column" height="100%">
<Box flexGrow="1" p={2}>
{typeof (text) == 'string' ? HTMLReactParser(text) : text}
</Box>
<Box className='Alert-footer'>
<DefaultButton className='Alert-margin' startIcon={<CloseIcon />} onClick={() => handleOkClose(onCancelClick)} autoFocus>{cancelLabel}</DefaultButton>
<DefaultButton className='Alert-margin' color={'error'} startIcon={<DeleteIcon/> } onClick={() => handleOkClose(onDeleteClick)}>{deleteLabel}</DefaultButton>
</Box>
</StyledBox>
);
},
{ isFullScreen: false, isResizeable: false, showFullScreen: false, isFullWidth: false, showTitle: true},
);
}
export default function ModalProvider({ children }) {
const [modals, setModals] = React.useState([]);
@ -143,7 +157,8 @@ export default function ModalProvider({ children }) {
const modalContext = React.useMemo(() => ({
...modalContextBase,
confirm: confirm.bind(modalContextBase),
alert: alert.bind(modalContextBase)
alert: alert.bind(modalContextBase),
confirmDelete: confirmDelete.bind(modalContextBase)
}), []);
return (
<ModalContext.Provider value={modalContext}>

View File

@ -175,29 +175,14 @@ class Notifier {
this.modal.alert(title, text, onOkClick, okLabel);
}
confirm(title, text, onOkClick, onCancelClick, okLabel=gettext('Yes'), cancelLabel=gettext('No'), extras=null) {
confirm(title, text, onOkClick, onCancelClick, okLabel=gettext('Yes'), cancelLabel=gettext('No'), okIcon) {
/* Use this if you want to use pgAdmin global notifier.
Or else, if you want to use modal inside iframe only then use ModalProvider eg- query tool */
this.modal.confirm(title, text, onOkClick, onCancelClick, okLabel, cancelLabel, extras);
this.modal.confirm(title, text, onOkClick, onCancelClick, okLabel, cancelLabel, okIcon);
}
confirmDelete(title, text, onOkClick, onCancelClick,okLabel = gettext('Yes'), cancelLabel = gettext('No')){
const extraButtons = (closeModal) => {
return [
{
type: 'default',
icon: <CheckRoundedIcon />,
label: okLabel,
onClick: ()=>{
onOkClick();
closeModal();
},
color: 'error',
},
];
};
this.modal.confirm(title, text, onOkClick, onCancelClick, okLabel, cancelLabel, extraButtons);
confirmDelete(title, text, onDeleteClick, onCancelClick, okLabel = gettext('Delete'), cancelLabel = gettext('Cancel')){
this.modal.confirmDelete(title, text, onDeleteClick, onCancelClick, okLabel, cancelLabel);
}
showModal(title, content, modalOptions) {

View File

@ -13,7 +13,7 @@ import gettext from 'sources/gettext';
import PropTypes from 'prop-types';
import { DefaultButton, PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
import { Box, Tooltip, CircularProgress } from '@mui/material';
import { ConnectedIcon, DisonnectedIcon } from '../../../../../../static/js/components/ExternalIcon';
import { ConnectedIcon, DisconnectedIcon } from '../../../../../../static/js/components/ExternalIcon';
const StyledBox = styled(Box)(({theme}) => ({
padding: '2px 4px',
@ -45,7 +45,7 @@ function ConnectionStatusIcon({status}) {
} else if(status == STATUS.CONNECTED || status == STATUS.FAILED) {
return <ConnectedIcon />;
} else {
return <DisonnectedIcon />;
return <DisconnectedIcon />;
}
}

View File

@ -11,7 +11,7 @@ import { styled } from '@mui/material/styles';
import { Box, CircularProgress, Tooltip } from '@mui/material';
import { DefaultButton, PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import { ConnectedIcon, DisonnectedIcon, QueryToolIcon } from '../../../../../../static/js/components/ExternalIcon';
import { ConnectedIcon, DisconnectedIcon, QueryToolIcon } from '../../../../../../static/js/components/ExternalIcon';
import { QueryToolContext } from '../QueryToolComponent';
import { CONNECTION_STATUS, CONNECTION_STATUS_MESSAGE } from '../QueryToolConstants';
import HourglassEmptyRoundedIcon from '@mui/icons-material/HourglassEmptyRounded';
@ -65,7 +65,7 @@ function ConnectionStatusIcon({connected, connecting, status}) {
return <ConnectedIcon />;
}
} else {
return <DisonnectedIcon />;
return <DisconnectedIcon />;
}
}