Fixed following: - Dropdown selected menu color - CodeMirror render on properties tab - Placeholders on select control - Codemirror borders

This commit is contained in:
Aditya Toshniwal
2021-08-24 11:51:47 +05:30
committed by Akshay Joshi
parent c35c72c551
commit 27e446a0b0
9 changed files with 68 additions and 69 deletions

View File

@@ -349,7 +349,7 @@ export default class ColumnSchema extends BaseUISchema {
},{ },{
id: 'attstorage', label: gettext('Storage'), group: gettext('Definition'), id: 'attstorage', label: gettext('Storage'), group: gettext('Definition'),
type: 'select', mode: ['properties', 'edit'], type: 'select', mode: ['properties', 'edit'],
cell: 'select', disabled: obj.inSchemaWithColumnCheck, first_empty: true, cell: 'select', disabled: obj.inSchemaWithColumnCheck,
controlProps: { placeholder: gettext('Select storage'), controlProps: { placeholder: gettext('Select storage'),
allowClear: false, allowClear: false,
}, },

View File

@@ -31,7 +31,7 @@ const useStyles = makeStyles((theme)=>({
height: '100%' height: '100%'
}, },
controlRow: { controlRow: {
paddingBottom: theme.spacing(1), marginBottom: theme.spacing(1),
}, },
nestedTabPanel: { nestedTabPanel: {
backgroundColor: theme.otherVars.headerBg, backgroundColor: theme.otherVars.headerBg,
@@ -62,7 +62,6 @@ function SQLTab({active, getSQLValue}) {
options={{ options={{
readOnly: true, readOnly: true,
}} }}
isAsync={true}
readonly={true} readonly={true}
/>; />;
} }
@@ -314,7 +313,7 @@ export default function FormView({
sqlTabActive = (Object.keys(tabs).length === tabValue); sqlTabActive = (Object.keys(tabs).length === tabValue);
/* Re-render and fetch the SQL tab when it is active */ /* Re-render and fetch the SQL tab when it is active */
tabs[sqlTabName] = [ tabs[sqlTabName] = [
useMemo(()=><SQLTab key="sqltab" active={sqlTabActive} getSQLValue={getSQLValue} />, [sqlTabActive]), useMemo(()=><SQLTab key="sqltab" active={sqlTabActive} getSQLValue={getSQLValue} />, [sqlTabActive, value]),
]; ];
tabsClassname[sqlTabName] = classes.fullSpace; tabsClassname[sqlTabName] = classes.fullSpace;
fullTabs.push(sqlTabName); fullTabs.push(sqlTabName);

View File

@@ -30,8 +30,8 @@ function MappedFormControlBase({type, value, id, onChange, className, visible, i
onChange && onChange(value); onChange && onChange(value);
}, []); }, []);
const onSqlChange = useCallback((e, cm) => { const onSqlChange = useCallback((value) => {
onChange && onChange(cm.getValue()); onChange && onChange(value);
}, []); }, []);
const onIntChange = useCallback((e) => { const onIntChange = useCallback((e) => {

View File

@@ -711,7 +711,7 @@ const usePropsStyles = makeStyles((theme)=>({
flexDirection: 'column' flexDirection: 'column'
}, },
controlRow: { controlRow: {
paddingBottom: theme.spacing(1), marginBottom: theme.spacing(1),
}, },
form: { form: {
padding: theme.spacing(1), padding: theme.spacing(1),
@@ -726,6 +726,9 @@ const usePropsStyles = makeStyles((theme)=>({
buttonMargin: { buttonMargin: {
marginRight: '0.5rem', marginRight: '0.5rem',
}, },
noPadding: {
padding: 0,
}
})); }));
/* If its the properties tab */ /* If its the properties tab */
@@ -757,7 +760,7 @@ function SchemaPropertiesView({
group = group || defaultTab; group = group || defaultTab;
if(field.isFullTab) { if(field.isFullTab) {
tabsClassname[group] = classes.fullSpace; tabsClassname[group] = classes.noPadding;
fullTabs.push(group); fullTabs.push(group);
} }
@@ -814,7 +817,7 @@ function SchemaPropertiesView({
readonly={readonly} readonly={readonly}
disabled={disabled} disabled={disabled}
visible={visible} visible={visible}
className={classes.controlRow} className={field.isFullTab ? null : classes.controlRow}
noLabel={field.isFullTab} noLabel={field.isFullTab}
/> />
); );
@@ -846,7 +849,7 @@ function SchemaPropertiesView({
> >
{tabName} {tabName}
</AccordionSummary> </AccordionSummary>
<AccordionDetails> <AccordionDetails className={tabsClassname[tabName]}>
<Box style={{width: '100%'}}> <Box style={{width: '100%'}}>
{finalTabs[tabName]} {finalTabs[tabName]}
</Box> </Box>

View File

@@ -7,71 +7,64 @@
// //
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useMemo, useRef } from 'react';
import {default as OrigCodeMirror} from 'bundled_codemirror'; import {default as OrigCodeMirror} from 'bundled_codemirror';
import {useOnScreen} from 'sources/custom_hooks'; import {useOnScreen} from 'sources/custom_hooks';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import CustomPropTypes from '../custom_prop_types';
/* React wrapper for CodeMirror */ /* React wrapper for CodeMirror */
export default function CodeMirror({currObj, name, value, options, events, ...props}) { export default function CodeMirror({currEditor, name, value, options, events, className}) {
const taRef = useRef(); const taRef = useRef();
const cmObj = useRef(); const editor = useRef();
const cmWrapper = useRef(); const cmWrapper = useRef();
const isVisibleTrack = useRef(); const isVisibleTrack = useRef();
useEffect(()=>{ useEffect(()=>{
/* Create the object only once on mount */ /* Create the object only once on mount */
cmObj.current = new OrigCodeMirror.fromTextArea( editor.current = new OrigCodeMirror.fromTextArea(
taRef.current, options); taRef.current, options);
currObj && currObj(cmObj.current); editor.current.setValue(value);
currEditor && currEditor(editor.current);
if(cmObj.current) { if(editor.current) {
try { try {
cmWrapper.current = cmObj.current.getWrapperElement(); cmWrapper.current = editor.current.getWrapperElement();
} catch(e) { } catch(e) {
cmWrapper.current = null; cmWrapper.current = null;
} }
} }
Object.keys(events||{}).forEach((eventName)=>{ Object.keys(events||{}).forEach((eventName)=>{
cmObj.current.on(eventName, events[eventName]); editor.current.on(eventName, events[eventName]);
}); });
}, []); }, []);
useEffect(()=>{ useMemo(() => {
/* Refresh when value changes async */ if(editor.current) {
if(props.isAsync) { editor.current.setValue(value);
if(cmObj.current) {
cmObj.current.setValue(value);
cmObj.current.refresh();
}
} }
}, [value]); }, [value]);
const onScreenVisible = useOnScreen(cmWrapper); const onScreenVisible = useOnScreen(cmWrapper);
if(!isVisibleTrack.current && onScreenVisible) { if(!isVisibleTrack.current && onScreenVisible) {
isVisibleTrack.current = true; isVisibleTrack.current = true;
editor.current?.refresh();
/* Refresh when value changes */
if(cmObj.current) {
cmObj.current.setValue(value);
cmObj.current.refresh();
}
cmObj.current.refresh();
} else if(!onScreenVisible) { } else if(!onScreenVisible) {
isVisibleTrack.current = false; isVisibleTrack.current = false;
} }
return <textarea ref={taRef} name={name} />; return (
<div className={className}><textarea ref={taRef} name={name} /></div>
);
} }
CodeMirror.propTypes = { CodeMirror.propTypes = {
currObj: PropTypes.func, currEditor: PropTypes.func,
name: PropTypes.string, name: PropTypes.string,
value: PropTypes.string, value: PropTypes.string,
options: PropTypes.object, options: PropTypes.object,
change: PropTypes.func, change: PropTypes.func,
events: PropTypes.object, events: PropTypes.object,
isAsync: PropTypes.bool className: CustomPropTypes.className,
}; };

View File

@@ -8,7 +8,7 @@
////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////
/* Common form components used in pgAdmin */ /* Common form components used in pgAdmin */
import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react'; import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { makeStyles } from '@material-ui/core/styles'; import { makeStyles } from '@material-ui/core/styles';
import { Box, FormControl, OutlinedInput, FormHelperText, import { Box, FormControl, OutlinedInput, FormHelperText,
Grid, IconButton, FormControlLabel, Switch, Checkbox, useTheme, InputLabel, Paper } from '@material-ui/core'; Grid, IconButton, FormControlLabel, Switch, Checkbox, useTheme, InputLabel, Paper } from '@material-ui/core';
@@ -59,7 +59,8 @@ const useStyles = makeStyles((theme) => ({
color: theme.palette.error.main, color: theme.palette.error.main,
}, },
sql: { sql: {
height: '100%', border: '1px solid ' + theme.otherVars.inputBorderColor,
borderRadius: theme.shape.borderRadius,
}, },
optionIcon: { optionIcon: {
...theme.mixins.nodeIcon, ...theme.mixins.nodeIcon,
@@ -139,17 +140,17 @@ FormInput.propTypes = {
export function InputSQL({value, options, onChange, readonly, ...props}) { export function InputSQL({value, options, onChange, readonly, ...props}) {
const classes = useStyles(); const classes = useStyles();
const cmObj = useRef(); const editor = useRef();
useEffect(()=>{ useEffect(()=>{
if(cmObj.current) { if(editor.current) {
cmObj.current.setOption('readOnly', readonly); editor.current.setOption('readOnly', readonly);
} }
}, [readonly]); }, [readonly]);
return ( return useMemo(()=>(
<CodeMirror <CodeMirror
currObj={(obj)=>cmObj.current=obj} currEditor={(obj)=>editor.current=obj}
value={value||''} value={value||''}
options={{ options={{
lineNumbers: true, lineNumbers: true,
@@ -159,12 +160,12 @@ export function InputSQL({value, options, onChange, readonly, ...props}) {
className={classes.sql} className={classes.sql}
events={{ events={{
change: (cm)=>{ change: (cm)=>{
onChange && onChange(cm.getValue(), cm); onChange && onChange(cm.getValue());
}, },
}} }}
{...props} {...props}
/> />
); ), [value]);
} }
InputSQL.propTypes = { InputSQL.propTypes = {
value: PropTypes.string, value: PropTypes.string,
@@ -581,18 +582,23 @@ const customReactSelectStyles = (theme, readonly)=>({
color: theme.palette.text.primary, color: theme.palette.text.primary,
boxShadow: 'none', boxShadow: 'none',
border: '1px solid ' + theme.otherVars.inputBorderColor, border: '1px solid ' + theme.otherVars.inputBorderColor,
marginTop: '2px',
}), }),
menuPortal: (provided)=>({ menuPortal: (provided)=>({
...provided, zIndex: 9999, ...provided, zIndex: 9999,
backgroundColor: 'inherit', backgroundColor: 'inherit',
color: 'inherit', color: 'inherit',
}), }),
option: (provided, state)=>({ option: (provided, state)=>{
...provided, return {
padding: '0.5rem', ...provided,
backgroundColor: state.isFocused ? theme.palette.grey[200] : padding: '0.5rem',
(state.isSelected ? theme.palette.primary.main : 'inherit'), color: 'inherit',
}), backgroundColor: state.isFocused ?
theme.palette.grey[400] : (state.isSelected ?
theme.palette.primary.light : 'inherit'),
};
},
multiValue: (provided)=>({ multiValue: (provided)=>({
...provided, ...provided,
backgroundColor: theme.palette.grey[400], backgroundColor: theme.palette.grey[400],
@@ -736,11 +742,12 @@ export function InputSelect({
openMenuOnClick: !readonly, openMenuOnClick: !readonly,
onChange: onChangeOption, onChange: onChangeOption,
isLoading: isLoading, isLoading: isLoading,
options: controlProps.allowSelectAll ? [{ label: 'Select All', value: '*' }, ...filteredOptions] : filteredOptions, options: controlProps.allowSelectAll ? [{ label: gettext('Select All'), value: '*' }, ...filteredOptions] : filteredOptions,
value: realValue, value: realValue,
menuPortalTarget: document.body, menuPortalTarget: document.body,
styles: styles, styles: styles,
inputId: cid, inputId: cid,
placeholder: controlProps.placeholder || gettext('Select...'),
...otherProps, ...otherProps,
...props ...props
}; };

View File

@@ -47,7 +47,7 @@ export function useOnScreen(ref) {
} }
); );
useEffect(() => { useEffect(() => {
if (ref.current) { if(ref?.current) {
observer.observe(ref.current); observer.observe(ref.current);
} }
// Remove the observer as soon as the component is unmounted // Remove the observer as soon as the component is unmounted

View File

@@ -19,14 +19,13 @@ describe('CodeMirror', ()=>{
let cmInstance, options={ let cmInstance, options={
lineNumbers: true, lineNumbers: true,
mode: 'text/x-pgsql', mode: 'text/x-pgsql',
}, cmObj = jasmine.createSpyObj('cmObj', {'setValue': ()=>{}, 'refresh': ()=>{}}); }, cmObj = jasmine.createSpyObj('cmObj', {'getValue':()=>'', 'setValue': ()=>{}, 'refresh': ()=>{}});
beforeEach(()=>{ beforeEach(()=>{
jasmineEnzyme(); jasmineEnzyme();
spyOn(OrigCodeMirror, 'fromTextArea').and.returnValue(cmObj); spyOn(OrigCodeMirror, 'fromTextArea').and.returnValue(cmObj);
cmInstance = mount( cmInstance = mount(
<CodeMirror <CodeMirror
value={'Init text'} value={'Init text'}
isAsync={true}
options={options} options={options}
className="testClass" className="testClass"
/>); />);
@@ -34,14 +33,12 @@ describe('CodeMirror', ()=>{
it('init', ()=>{ it('init', ()=>{
/* textarea ref passed to fromTextArea */ /* textarea ref passed to fromTextArea */
expect(OrigCodeMirror.fromTextArea).toHaveBeenCalledWith(cmInstance.getDOMNode(), options); expect(OrigCodeMirror.fromTextArea).toHaveBeenCalledWith(cmInstance.find('textarea').getDOMNode(), options);
expect(cmObj.setValue).toHaveBeenCalledWith('Init text'); expect(cmObj.setValue).toHaveBeenCalledWith('Init text');
expect(cmObj.refresh).toHaveBeenCalled();
}); });
it('change value', ()=>{ it('change value', ()=>{
cmInstance.setProps({value: 'the new text'}); cmInstance.setProps({value: 'the new text'});
expect(cmObj.setValue).toHaveBeenCalledWith('the new text'); expect(cmObj.setValue).toHaveBeenCalledWith('the new text');
expect(cmObj.refresh).toHaveBeenCalled();
}); });
}); });

View File

@@ -9,15 +9,15 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
// beforeAll(function () { beforeAll(function () {
// spyOn(console, 'warn').and.callThrough(); spyOn(console, 'warn').and.callThrough();
// spyOn(console, 'error').and.callThrough(); spyOn(console, 'error').and.callThrough();
// }); });
// afterEach(function (done) { afterEach(function (done) {
// setTimeout(function () { setTimeout(function () {
// expect(console.warn).not.toHaveBeenCalled(); expect(console.warn).not.toHaveBeenCalled();
// expect(console.error).not.toHaveBeenCalled(); expect(console.error).not.toHaveBeenCalled();
// done(); done();
// }, 0); }, 0);
// }); });