Skip to content

Commit

Permalink
Merge pull request #125 from VEuPathDB/refactor-styleOverrides
Browse files Browse the repository at this point in the history
Refactor CheckboxTree styling
  • Loading branch information
jernestmyers committed Nov 14, 2022
2 parents e1e58f1 + dc7881a commit 5a6000e
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 53 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@veupathdb/coreui",
"version": "0.18.13",
"version": "0.18.14",
"author": "Michael Dunn <mdunn4@nd.edu>",
"description": "Core components and style definitions for VEuPath applications.",
"private": false,
Expand Down
61 changes: 44 additions & 17 deletions src/components/inputs/SearchBox/SearchBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,18 @@ import { merge } from 'lodash';
import { safeHtml } from '../SelectTree/Utils';
import { Close, Filter } from '../../icons';
import { Help, Search } from '@material-ui/icons';
import { Tooltip } from '@material-ui/core';
import { Theme, Tooltip, withStyles } from '@material-ui/core';

const StyledTooltip = withStyles((theme: Theme) => ({
tooltip: {
backgroundColor: '#fffde7',
color: 'rgba(0, 0, 0, 0.87)',
maxWidth: 320,
fontSize: theme.typography.pxToRem(12),
border: '1px solid #dadde9',
boxShadow: theme.shadows[1],
},
}))(Tooltip);

type Props = {

Expand All @@ -21,7 +32,10 @@ type Props = {
placeholderText?: string;

/** Icon name to show in input box. Defaults to "search". */
iconName?: string;
iconName?: 'search' | 'filter';

/** Specifies where icon should be placed within input box. Defaults to "right". */
iconPosition?: 'left' | 'right';

/** Text to appear as tooltip of help icon, should describe how the search is performed. Defaults to empty (no icon) */
helpText?: string;
Expand Down Expand Up @@ -50,7 +64,7 @@ const searchIconStyleSpec = {
const filterIconStyleSpec = {
fill: '#999999',
position: 'relative',
left: '5px',
top: '2px',
fontSize: '1.5em',
}

Expand All @@ -60,6 +74,7 @@ const defaultStyleSpec: SearchBoxStyleSpec = {
height: '0.7em',
color: '#17b',
marginLeft: '0.25em',
cursor: 'pointer',
},
optionalIcon: {},
clearSearchIcon: {
Expand All @@ -79,15 +94,16 @@ const defaultStyleSpec: SearchBoxStyleSpec = {
input: {
border: '1px solid #888',
borderRadius: '16px',
padding: '0.2em 1.5em 0.2em 1em',
width: 'calc(100% - 2.5em)',
width: 'calc(100% - 3em)',
textOverflow: 'ellipsis',
background: '#fff',
},
container: {
display: 'flex',
alignItems: 'center',
whiteSpace: 'nowrap',
width: '100%',
margin: '0 0.5em 0 2em',
margin: '0 0.5em',
}
}

Expand All @@ -103,16 +119,23 @@ export default function SearchBox({
placeholderText = '',
helpText = '',
iconName = 'search',
iconPosition = 'right',
styleOverrides = {},
}: Props) {

const styleSpec: SearchBoxStyleSpec = useMemo(() => {
const defaultStyleWithIcon = {
const defaultStyleWithIconSpecs = {
...defaultStyleSpec,
optionalIcon: iconName === 'search' ? {...searchIconStyleSpec} : {...filterIconStyleSpec}
input: {
...defaultStyleSpec.input,
padding: iconPosition === 'right' ? '0.2em 2em 0.2em 1em' : '0.2em 1em 0.2em 2em',
},
optionalIcon:
iconName === 'search' ? {...searchIconStyleSpec} :
iconPosition === 'right' ? {...filterIconStyleSpec, right: '25px'} : {...filterIconStyleSpec, left: '5px'}
}
return merge({}, defaultStyleWithIcon, styleOverrides)
}, [styleOverrides])
return merge({}, defaultStyleWithIconSpecs, styleOverrides)
}, [styleOverrides, iconName, iconPosition])

function handleSearchTermChange(e: React.ChangeEvent<HTMLInputElement>) {
let searchTerm = e.currentTarget.value;
Expand All @@ -130,10 +153,13 @@ export default function SearchBox({
onSearchTermChange!('');
}

const optionalIcon = iconName === 'filter' ? (
<Filter style={styleSpec.optionalIcon} />
) : (
/**
* At this point we only use two different icons in search bars: 'filter' and 'search'
*/
const optionalIcon = iconName === 'search' ? (
<Search style={styleSpec.optionalIcon} />
) : (
<Filter style={styleSpec.optionalIcon} />
);

return (
Expand All @@ -143,7 +169,7 @@ export default function SearchBox({
<label css={{
flexGrow: 1,
}}>
{iconName === 'filter' && !searchTerm ?
{iconPosition === 'left' && !searchTerm ?
<span style={{
position: 'absolute',
height: 0,
Expand Down Expand Up @@ -174,7 +200,7 @@ export default function SearchBox({
>
<Close style={styleSpec.clearSearchIcon} />
</button> :
iconName === 'search' ?
iconPosition === 'right' ?
<span style={{
position: 'absolute',
height: 0,
Expand All @@ -188,11 +214,12 @@ export default function SearchBox({
{/* use safeHtml for helpText to allow italic */}
{!helpText ?
null :
<Tooltip
<StyledTooltip
title={safeHtml(helpText)}
interactive
>
<Help style={styleSpec.helpIcon}/>
</Tooltip>}
</StyledTooltip>}
</div>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/components/inputs/SelectTree/SelectTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ function SelectTree<T>(props: SelectTreeProps<T>) {
additionalFilters={props.additionalFilters}
isAdditionalFilterApplied={props.isAdditionalFilterApplied}
wrapTreeSection={props.wrapTreeSection}
styleOverrides={props.styleOverrides}
/>
)

Expand Down
3 changes: 3 additions & 0 deletions src/components/inputs/SelectTree/Utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ export function safeHtml<P>(str: string, props?: P, Component?: React.ComponentC
export function safeHtml<P>(str: string, props?: P, Component?: React.StatelessComponent<P>): JSX.Element;
export function safeHtml<P>(str: string, props?: P, Component?: string): JSX.Element;
export function safeHtml<P>(str = '', props?: P, Component: any = 'span'): JSX.Element {
if (str.indexOf('<') === -1) {
return <Component {...props}>{str}</Component>
}
// Use innerHTML to auto close tags
let container = document.createElement('div');
container.innerHTML = str;
Expand Down
53 changes: 38 additions & 15 deletions src/components/inputs/checkboxes/CheckboxTree/CheckboxTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ const defaultTreeLinksStyleSpec: TreeLinksStyleSpec = {
container: {
display: 'flex',
justifyContent: 'center',
height: '2em',
height: 'auto',
flexWrap: 'wrap',
padding: '0.5em 0',
rowGap: '0.5em',
},
links: {
fontSize: '0.9em',
Expand All @@ -43,7 +45,9 @@ const defaultTreeLinksStyleSpec: TreeLinksStyleSpec = {
padding: 0,
margin: 0,
},
actionsContainerStyle: {}
actionsContainerStyle: {
flexGrow: 1,
}
};

const linksHoverDecoration = css({
Expand All @@ -54,7 +58,11 @@ const linksHoverDecoration = css({

export type CheckboxTreeStyleSpec = {
treeLinks?: TreeLinksStyleSpec;
searchAndFilterWrapper?: React.CSSProperties;
searchBox?: SearchBoxStyleSpec;
additionalFilters?: {
container?: React.CSSProperties;
};
treeNode?: CheckboxTreeNodeStyleSpec;
treeSection?: {
container?: React.CSSProperties;
Expand All @@ -64,19 +72,30 @@ export type CheckboxTreeStyleSpec = {

const defaultCheckboxTreeStyleSpec: CheckboxTreeStyleSpec = {
treeLinks: defaultTreeLinksStyleSpec,
treeNode: {},
searchAndFilterWrapper: {
display: 'flex',
justifyContent: 'center',
},
searchBox: {},
additionalFilters: {
container: {
display: 'flex',
alignItems: 'center',
},
},
treeSection: {
container: {
flexGrow: 2,
overflowY: 'auto'
overflowY: 'auto',
margin: '0.5em 0',
},
ul: {
width: '100%',
margin: '0.5em 0',
width: '100%',
margin: 0,
padding: '0 1em',
}
}
},
treeNode: {},
}

type StatefulNode<T> = T & {
Expand Down Expand Up @@ -166,7 +185,10 @@ export type CheckboxTreeProps<T> = {
searchBoxPlaceholder: string;

/** Name of icon to show in search box */
searchIconName?: string;
searchIconName?: 'search' | 'filter';

/** Position of icon in search box */
searchIconPosition?: 'left' | 'right';

/** Search box help text: if present, a help icon will appear; mouseover the icon and a tooltip will appear with this text */
searchBoxHelp?: string;
Expand Down Expand Up @@ -332,17 +354,18 @@ function getInitialNodeState<T>(node: T, getNodeChildren: (t: T) => T[]) {

interface AdditionalFiltersProps {
filters?: React.ReactNode[];
filtersStyleSpec?: React.CSSProperties;
}

/**
* Renders additional filters to supplement the default searchbox
*/
function AdditionalFilters({ filters }: AdditionalFiltersProps) {
function AdditionalFilters({ filters, filtersStyleSpec }: AdditionalFiltersProps) {
return (
<>
{
filters != null && filters.length > 0 &&
<div>
<div css={{...filtersStyleSpec}}>
{
filters.map((filter, index) => (
<span key={index}>
Expand Down Expand Up @@ -600,6 +623,7 @@ function CheckboxTree<T> (props: CheckboxTreeProps<T>) {
onSearchTermChange,
searchBoxPlaceholder,
searchIconName,
searchIconPosition,
searchBoxHelp,
additionalFilters,
wrapTreeSection,
Expand All @@ -622,7 +646,7 @@ function CheckboxTree<T> (props: CheckboxTreeProps<T>) {
isLeafVisible,
generated: generatedTreeState,
}
}, [tree, isSearchable, searchTerm, searchPredicate, isAdditionalFilterApplied, name, getNodeId, getNodeChildren, props.renderNode, expandedList]);
}, [tree, isSearchable, searchTerm, searchPredicate, isAdditionalFilterApplied, name, getNodeId, getNodeChildren, props.renderNode, expandedList, selectedList]);


/**
Expand Down Expand Up @@ -838,21 +862,20 @@ function CheckboxTree<T> (props: CheckboxTreeProps<T>) {
<>
{linksPosition && linksPosition == LinksPosition.Top ? treeLinks : null}
{!isSearchable || !showSearchBox ? "" : (
<div css={{
display: 'flex',
justifyContent: 'center',
}}>
<div css={{...styleSpec.searchAndFilterWrapper}}>
<SearchBox
autoFocus={autoFocusSearchBox}
searchTerm={searchTerm}
onSearchTermChange={onSearchTermChange}
placeholderText={searchBoxPlaceholder}
iconName={searchIconName}
iconPosition={searchIconPosition}
helpText={searchBoxHelp}
styleOverrides={styleSpec.searchBox}
/>
<AdditionalFilters
filters={additionalFilters}
filtersStyleSpec={styleSpec.additionalFilters?.container}
/>
</div>
)}
Expand Down
Loading

0 comments on commit 5a6000e

Please sign in to comment.