Skip to content

Commit

Permalink
Fixed #2397 - Improve custom icon support (#2335)
Browse files Browse the repository at this point in the history
* improve custom icon support

* make sure custom icons don't add weird incorrect classNames
  • Loading branch information
amartyn-everlaw committed Nov 6, 2021
1 parent 7d5545f commit a42c765
Show file tree
Hide file tree
Showing 38 changed files with 162 additions and 115 deletions.
5 changes: 3 additions & 2 deletions src/components/accordion/Accordion.d.ts
@@ -1,4 +1,5 @@
import * as React from 'react';
import {IconType} from "../utils/Utils";

type AccordionTabHeaderTemplateType = React.ReactNode | ((props: AccordionTabProps) => React.ReactNode);

Expand Down Expand Up @@ -27,8 +28,8 @@ export interface AccordionProps {
className?: string;
style?: object;
multiple?: boolean;
expandIcon?: string;
collapseIcon?: string;
expandIcon?: IconType<AccordionProps>;
collapseIcon?: IconType<AccordionProps>;
transitionOptions?: object;
onTabOpen?(e: AccordionEventParams): void;
onTabClose?(e: AccordionEventParams): void;
Expand Down
9 changes: 5 additions & 4 deletions src/components/accordion/Accordion.js
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { classNames, ObjectUtils, UniqueComponentId } from '../utils/Utils';
import { classNames, ObjectUtils, IconUtils, UniqueComponentId } from '../utils/Utils';
import { CSSTransition } from '../csstransition/CSSTransition';

export class AccordionTab extends Component {
Expand Down Expand Up @@ -48,8 +48,8 @@ export class Accordion extends Component {
className: PropTypes.string,
style: PropTypes.object,
multiple: PropTypes.bool,
expandIcon: PropTypes.string,
collapseIcon: PropTypes.string,
expandIcon: PropTypes.any,
collapseIcon: PropTypes.any,
transitionOptions: PropTypes.object,
onTabOpen: PropTypes.func,
onTabClose: PropTypes.func,
Expand Down Expand Up @@ -134,11 +134,12 @@ export class Accordion extends Component {
const ariaControls = this.state.id + '_content_' + index;
const tabIndex = tab.props.disabled ? -1 : null;
const header = tab.props.headerTemplate ? ObjectUtils.getJSXElement(tab.props.headerTemplate, tab.props) : <span className="p-accordion-header-text">{tab.props.header}</span>;
const icon = selected ? this.props.collapseIcon : this.props.expandIcon;

return (
<div className={tabHeaderClass} style={tab.props.headerStyle}>
<a href={'#' + ariaControls} id={id} className="p-accordion-header-link" aria-controls={ariaControls} role="tab" aria-expanded={selected} onClick={(event) => this.onTabHeaderClick(event, tab, index)} tabIndex={tabIndex}>
<span className={iconClassName}></span>
{IconUtils.getJSXIcon(icon, {className: iconClassName}, this.props)}
{header}
</a>
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/components/autocomplete/AutoComplete.d.ts
@@ -1,6 +1,7 @@
import * as React from 'react';
import TooltipOptions from '../tooltip/tooltipoptions';
import { VirtualScrollerProps } from '../virtualscroller';
import {IconType} from "../utils/Utils";

type AutoCompleteOptionGroupTemplateType = React.ReactNode | ((suggestion: any, index: number) => React.ReactNode);

Expand Down Expand Up @@ -83,7 +84,7 @@ export interface AutoCompleteProps {
itemTemplate?: AutoCompleteItemTemplateType;
selectedItemTemplate?: AutoCompleteSelectedItemTemplateType;
transitionOptions?: object;
dropdownIcon?: string;
dropdownIcon?: IconType<AutoCompleteProps>;
onChange?(e: AutoCompleteChangeParams): void;
onFocus?(event: React.FocusEvent<HTMLInputElement>): void;
onBlur?(event: React.FocusEvent<HTMLInputElement>): void;
Expand Down
2 changes: 1 addition & 1 deletion src/components/autocomplete/AutoComplete.js
Expand Up @@ -112,7 +112,7 @@ export class AutoComplete extends Component {
itemTemplate: PropTypes.any,
selectedItemTemplate: PropTypes.any,
transitionOptions: PropTypes.object,
dropdownIcon: PropTypes.string,
dropdownIcon: PropTypes.any,
onChange: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
Expand Down
3 changes: 2 additions & 1 deletion src/components/avatar/Avatar.d.ts
@@ -1,4 +1,5 @@
import * as React from 'react';
import {IconType} from "../utils/Utils";

type AvatarSizeType = 'normal' | 'large' | 'xlarge';

Expand All @@ -8,7 +9,7 @@ type AvatarTemplateType = React.ReactNode | ((props: AvatarProps) => React.React

export interface AvatarProps {
label?: string;
icon?: string;
icon?: IconType<AvatarProps>;
image?: string;
size?: AvatarSizeType;
shape?: AvatarShapeType;
Expand Down
6 changes: 3 additions & 3 deletions src/components/avatar/Avatar.js
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { ObjectUtils, classNames } from '../utils/Utils';
import {ObjectUtils, classNames, IconUtils} from '../utils/Utils';

export class Avatar extends Component {

Expand All @@ -20,7 +20,7 @@ export class Avatar extends Component {

static propTypes = {
label: PropTypes.string,
icon: PropTypes.string,
icon: PropTypes.any,
image: PropTypes.string,
size: PropTypes.string,
shape: PropTypes.string,
Expand All @@ -38,7 +38,7 @@ export class Avatar extends Component {
}
else if (this.props.icon) {
const iconClassName = classNames('p-avatar-icon', this.props.icon);
return <span className={iconClassName}></span>;
return IconUtils.getJSXIcon(this.props.icon, {className: iconClassName}, this.props);
}
else if (this.props.image) {
const onError = (e) => {
Expand Down
13 changes: 3 additions & 10 deletions src/components/button/Button.d.ts
@@ -1,27 +1,20 @@
import * as React from 'react';
import TooltipOptions from '../tooltip/tooltipoptions';
import {IconType} from "../utils/Utils";

type ButtonPositionType = 'top' | 'bottom' | 'left' | 'right';

type ButtonIconType = React.ReactNode | ((options: ButtonIconOptions) => React.ReactNode);

export interface ButtonIconOptions {
className: string;
element: React.ReactNode;
props: ButtonProps;
}

export interface ButtonProps extends Omit<React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, 'disabled'|'ref'> {
label?: string;
icon?: ButtonIconType;
icon?: IconType<ButtonProps>;
iconPos?: ButtonPositionType;
badge?: string;
badgeClassName?: string;
tooltip?: string;
tooltipOptions?: TooltipOptions;
disabled?: boolean;
loading?: boolean;
loadingIcon?: ButtonIconType;
loadingIcon?: IconType<ButtonProps>;
}

export declare class Button extends React.Component<ButtonProps, any> { }
38 changes: 11 additions & 27 deletions src/components/button/Button.js
@@ -1,6 +1,6 @@
import React, { Component, createRef } from 'react';
import PropTypes from 'prop-types';
import { ObjectUtils, classNames } from '../utils/Utils';
import { ObjectUtils, classNames, IconUtils } from '../utils/Utils';
import { tip } from '../tooltip/Tooltip';
import { Ripple } from '../ripple/Ripple';

Expand Down Expand Up @@ -91,32 +91,16 @@ export class ButtonComponent extends Component {

renderIcon() {
let icon = this.props.loading ? this.props.loadingIcon : this.props.icon;
let content = null;

if (icon) {
let iconType = typeof icon;
let className = classNames('p-button-icon p-c', {
'p-button-loading-icon': this.props.loading,
[`${icon}`]: iconType === 'string',
'p-button-icon-left': this.props.iconPos === 'left' && this.props.label,
'p-button-icon-right': this.props.iconPos === 'right' && this.props.label,
'p-button-icon-top': this.props.iconPos === 'top' && this.props.label,
'p-button-icon-bottom': this.props.iconPos === 'bottom' && this.props.label,
});
content = <span className={className}></span>;

if (iconType !== 'string') {
const defaultContentOptions = {
className,
element: content,
props: this.props
};

content = ObjectUtils.getJSXElement(icon, defaultContentOptions);
}
}

return content;
let iconType = typeof icon;
let className = classNames('p-button-icon p-c', {
'p-button-loading-icon': this.props.loading,
[`${icon}`]: iconType === 'string',

This comment has been minimized.

Copy link
@kalinkrustev

kalinkrustev Nov 11, 2021

Contributor

Is this still needed? Seems icon classes are applied twice.
Observed at the showcase:
image

'p-button-icon-left': this.props.iconPos === 'left' && this.props.label,
'p-button-icon-right': this.props.iconPos === 'right' && this.props.label,
'p-button-icon-top': this.props.iconPos === 'top' && this.props.label,
'p-button-icon-bottom': this.props.iconPos === 'bottom' && this.props.label,
});
return IconUtils.getJSXIcon(icon, {className}, this.props);
}

renderLabel() {
Expand Down
3 changes: 2 additions & 1 deletion src/components/calendar/Calendar.d.ts
@@ -1,5 +1,6 @@
import * as React from 'react';
import TooltipOptions from '../tooltip/tooltipoptions';
import {IconType} from "../utils/Utils";

type CalendarAppendToType = 'self' | HTMLElement | undefined | null;

Expand Down Expand Up @@ -84,7 +85,7 @@ export interface CalendarProps {
tabIndex?: number;
placeholder?: string;
showIcon?: boolean;
icon?: string;
icon?: IconType<CalendarProps>;
showOnFocus?: boolean;
numberOfMonths?: number;
view?: string;
Expand Down
2 changes: 1 addition & 1 deletion src/components/calendar/Calendar.js
Expand Up @@ -116,7 +116,7 @@ export class Calendar extends Component {
tabIndex: PropTypes.number,
placeholder: PropTypes.string,
showIcon: PropTypes.bool,
icon: PropTypes.string,
icon: PropTypes.any,
showOnFocus: PropTypes.bool,
numberOfMonths: PropTypes.number,
view: PropTypes.string,
Expand Down
3 changes: 2 additions & 1 deletion src/components/checkbox/Checkbox.d.ts
@@ -1,5 +1,6 @@
import * as React from 'react';
import TooltipOptions from '../tooltip/tooltipoptions';
import {IconType} from "../utils/Utils";

interface CheckboxChangeTargetOptions {
type: 'checkbox';
Expand Down Expand Up @@ -33,7 +34,7 @@ export interface CheckboxProps {
required?: boolean;
readOnly?: boolean;
tabIndex?: number;
icon?: string;
icon?: IconType<CheckboxProps>;
tooltip?: string;
tooltipOptions?: TooltipOptions;
ariaLabelledBy?: string;
Expand Down
6 changes: 3 additions & 3 deletions src/components/checkbox/Checkbox.js
@@ -1,6 +1,6 @@
import React, { Component, createRef } from 'react';
import PropTypes from 'prop-types';
import { classNames } from '../utils/Utils';
import {classNames, IconUtils} from '../utils/Utils';
import { tip } from '../tooltip/Tooltip';

export class Checkbox extends Component {
Expand Down Expand Up @@ -44,7 +44,7 @@ export class Checkbox extends Component {
required: PropTypes.bool,
readOnly: PropTypes.bool,
tabIndex: PropTypes.number,
icon: PropTypes.string,
icon: PropTypes.any,
tooltip: PropTypes.string,
tooltipOptions: PropTypes.object,
ariaLabelledBy: PropTypes.string,
Expand Down Expand Up @@ -181,7 +181,7 @@ export class Checkbox extends Component {
onKeyDown={this.onKeyDown} onFocus={this.onFocus} onBlur={this.onBlur} disabled={this.props.disabled} readOnly={this.props.readOnly} required={this.props.required}/>
</div>
<div className={boxClass} ref={el => this.box = el} role="checkbox" aria-checked={this.isChecked()}>
<span className={iconClass}></span>
{IconUtils.getJSXIcon(this.props.icon, {className: iconClass}, this.props)}
</div>
</div>
)
Expand Down
7 changes: 3 additions & 4 deletions src/components/chip/Chip.d.ts
@@ -1,16 +1,15 @@
import * as React from 'react';

type ChipTemplateType = React.ReactNode | ((props: ChipProps) => React.ReactNode);
import {IconType, TemplateType} from "../utils/Utils";

export interface ChipProps {
label?: string;
icon?: string;
icon?: IconType<ChipProps>;
image?: string;
removable?: boolean;
removeIcon?: string;
className?: string;
style?: object;
template?: ChipTemplateType;
template?: TemplateType<ChipProps>;
imageAlt?: string;
onImageError?(event: React.SyntheticEvent): void;
onRemove?(event: React.MouseEvent<HTMLElement>): void;
Expand Down
13 changes: 8 additions & 5 deletions src/components/chip/Chip.js
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { classNames, ObjectUtils } from '../utils/Utils';
import { classNames, ObjectUtils, IconUtils } from '../utils/Utils';

export class Chip extends Component {

Expand All @@ -20,10 +20,10 @@ export class Chip extends Component {

static propTypes = {
label: PropTypes.string,
icon: PropTypes.string,
icon: PropTypes.any,
image: PropTypes.string,
removable: PropTypes.bool,
removeIcon: PropTypes.string,
removeIcon: PropTypes.any,
className: PropTypes.string,
style: PropTypes.object,
template: PropTypes.any,
Expand Down Expand Up @@ -73,7 +73,7 @@ export class Chip extends Component {
}
else if (this.props.icon) {
const iconClassName = classNames('p-chip-icon', this.props.icon);
content.push(<span key="icon" className={iconClassName}></span>);
content.push(IconUtils.getJSXIcon(this.props.icon, {key: "icon", className: iconClassName}, this.props));
}

if (this.props.label) {
Expand All @@ -82,7 +82,10 @@ export class Chip extends Component {

if (this.props.removable) {
const removableIconClassName = classNames('p-chip-remove-icon', this.props.removeIcon);
content.push(<span key="removeIcon" tabIndex={0} className={removableIconClassName} onClick={this.close} onKeyDown={this.onKeyDown}></span>);
content.push(IconUtils.getJSXIcon(this.props.removeIcon,
{key: "removeIcon", tabIndex: 0, className: removableIconClassName, onClick: this.close, onKeyDown: this.onKeyDown},
this.props,
))
}

return content;
Expand Down
7 changes: 4 additions & 3 deletions src/components/confirmdialog/ConfirmDialog.d.ts
@@ -1,5 +1,6 @@
import * as React from 'react';
import { DialogProps } from '../dialog';
import {IconType} from "../utils/Utils";

type ConfirmDialogTemplateType = React.ReactNode | ((options: ConfirmDialogOptions) => React.ReactNode);

Expand All @@ -26,9 +27,9 @@ export interface ConfirmDialogProps extends Omit<DialogProps, 'onHide'> {
message?: ConfirmDialogTemplateType;
rejectLabel?: string;
acceptLabel?: string;
icon?: string;
rejectIcon?: string;
acceptIcon?: string;
icon?: IconType<ConfirmDialogProps>;
rejectIcon?: IconType<ConfirmDialogProps>;;
acceptIcon?: IconType<ConfirmDialogProps>;;
rejectClassName?: string;
acceptClassName?: string;
appendTo?: ConfirmDialogAppendToType;
Expand Down
10 changes: 5 additions & 5 deletions src/components/confirmdialog/ConfirmDialog.js
@@ -1,7 +1,7 @@
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { DomHandler, ObjectUtils, classNames } from '../utils/Utils';
import {DomHandler, ObjectUtils, classNames, IconUtils} from '../utils/Utils';
import { Dialog } from '../dialog/Dialog';
import { Button } from '../button/Button';
import { localeOption } from '../api/Api';
Expand Down Expand Up @@ -67,9 +67,9 @@ export class ConfirmDialog extends Component {
message: PropTypes.any,
rejectLabel: PropTypes.string,
acceptLabel: PropTypes.string,
icon: PropTypes.string,
rejectIcon: PropTypes.string,
acceptIcon: PropTypes.string,
icon: PropTypes.any,
rejectIcon: PropTypes.any,
acceptIcon: PropTypes.any,
rejectClassName: PropTypes.string,
acceptClassName: PropTypes.string,
appendTo: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
Expand Down Expand Up @@ -175,7 +175,7 @@ export class ConfirmDialog extends Component {

return (
<Dialog visible={this.state.visible} {...dialogProps} className={className} footer={footer} onHide={this.hide} breakpoints={this.props.breakpoints}>
<i className={iconClassName} />
{IconUtils.getJSXIcon(this.props.icon, {className: iconClassName}, this.props)}
<span className="p-confirm-dialog-message">{message}</span>
</Dialog>
);
Expand Down
7 changes: 4 additions & 3 deletions src/components/confirmpopup/ConfirmPopup.d.ts
@@ -1,4 +1,5 @@
import * as React from 'react';
import {IconType} from "../utils/Utils";

type ConfirmPopupTemplateType = React.ReactNode | ((options: ConfirmPopupOptions) => React.ReactNode);

Expand All @@ -23,9 +24,9 @@ export interface ConfirmPopupProps {
message?: ConfirmPopupTemplateType;
rejectLabel?: string;
acceptLabel?: string;
icon?: string;
rejectIcon?: string;
acceptIcon?: string;
icon?: IconType<ConfirmPopupProps>;
rejectIcon?: IconType<ConfirmPopupProps>;
acceptIcon?: IconType<ConfirmPopupProps>;
rejectClassName?: string;
acceptClassName?: string;
className?: string;
Expand Down

0 comments on commit a42c765

Please sign in to comment.