Skip to content

Commit

Permalink
fix: cannot toggle custom toolbar state(active, disabled) (nhn#1639)
Browse files Browse the repository at this point in the history
* fix: wron toolbarItems type definition

* refactor: lift up changing toolbar state logic to hoc

* chore: add test case(custom toolbar item active, disabled state)

* chore: change toolbar-item-wrapper css name

* chore: apply code review

* chore: apply code review - 2
  • Loading branch information
js87zz authored Jul 6, 2021
1 parent aa77db2 commit d72539d
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 43 deletions.
39 changes: 38 additions & 1 deletion src/__test__/integration/ui/toolbar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,8 @@ describe('custom button toolbar', () => {
});

describe('custom toolbar element', () => {
const onUpdatedSpy = jest.fn();

function createCustomItem() {
const el = document.createElement('div');

Expand All @@ -503,6 +505,8 @@ describe('custom toolbar element', () => {
el,
name: 'myToolbar',
tooltip: 'custom1!',
state: 'strong',
onUpdated: onUpdatedSpy,
};
}

Expand Down Expand Up @@ -541,6 +545,14 @@ describe('custom toolbar element', () => {
};
}

function clickMarkdownWriteTab() {
document.querySelectorAll<HTMLElement>('.tab-item')![0].click();
}

function clickMarkdownPreviewTab() {
document.querySelectorAll<HTMLElement>('.tab-item')![1].click();
}

const customItem = createCustomItem();
const customItemWithPopup = createCustomItemWithPopup();

Expand All @@ -550,14 +562,15 @@ describe('custom toolbar element', () => {
container = document.createElement('div');
document.body.appendChild(container);

onUpdatedSpy.mockReset();
em = new EventEmitter();

destroy = render(
container,
html`
<${Toolbar}
eventEmitter=${em}
previewStyle="vertical"
previewStyle="tab"
toolbarItems=${toolbarItems}
editorType="markdown"
/>
Expand Down Expand Up @@ -605,6 +618,30 @@ describe('custom toolbar element', () => {

expect(spy).toHaveBeenCalledWith('heading', { level: 3 });
});

it('should toggle active state properly when toolbar state is changed', () => {
em.emit('changeToolbarState', { toolbarState: { strong: true } });

expect(onUpdatedSpy).toHaveBeenCalledWith(expect.objectContaining({ active: true }));

em.emit('changeToolbarState', { toolbarState: { strong: false } });

expect(onUpdatedSpy).toHaveBeenCalledWith(expect.objectContaining({ active: false }));
});

it('should toggle disabled state when changing markdown tab mode', () => {
em.emit('changePreviewStyle', 'tab');

// change markdown tab mode to preview tab
clickMarkdownPreviewTab();

expect(onUpdatedSpy).toHaveBeenCalledWith(expect.objectContaining({ disabled: true }));

// change markdown tab mode to write tab
clickMarkdownWriteTab();

expect(onUpdatedSpy).toHaveBeenCalledWith(expect.objectContaining({ disabled: false }));
});
});

describe('API', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/css/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@
display: flex;
}

.tui-toolbar-item-wrapper {
.toastui-editor-toolbar-item-wrapper {
margin: 7px 5px;
height: 32px;
line-height: 32px;
Expand Down
4 changes: 2 additions & 2 deletions src/ui/components/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ export class Layout extends Component<Props, State> {
const { eventEmitter, hideModeSwitch, toolbarItems, theme } = this.props;
const { hide, previewStyle, editorType } = this.state;
const displayClassName = hide ? ' hidden' : '';
const editorTypeClassName = editorType === 'markdown' ? cls('md-mode') : cls('ww-mode');
const editorTypeClassName = cls(editorType === 'markdown' ? 'md-mode' : 'ww-mode');
const previewClassName = `${cls('md')}-${previewStyle}-style`;
const themeClassName = theme === 'light' ? '' : `${cls(theme)} `;
const themeClassName = cls([theme !== 'light', `${theme} `]);

return html`
<div
Expand Down
30 changes: 29 additions & 1 deletion src/ui/components/toolbar/buttonHoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
SetItemWidth,
ComponentClass,
ToolbarButtonInfo,
ToolbarState,
} from '@t/ui';
import { Emitter } from '@t/event';
import html from '@/ui/vdom/template';
Expand All @@ -22,10 +23,36 @@ interface Props {
setItemWidth?: SetItemWidth;
}

interface Payload {
toolbarState: ToolbarState;
}

interface State {
active: boolean;
}

const TOOLTIP_INDENT = 6;

export function connectHOC(WrappedComponent: ComponentClass) {
return class ButtonHOC extends Component<Props> {
return class ButtonHOC extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { active: false };
this.addEvent();
}

private addEvent() {
const { item, eventEmitter } = this.props;

if (item.state) {
eventEmitter.listen('changeToolbarState', ({ toolbarState }: Payload) => {
const active = !!toolbarState[item.state!];

this.setState({ active });
});
}
}

private getBound(el: HTMLElement) {
const { offsetLeft, offsetTop } = getTotalOffset(
el,
Expand Down Expand Up @@ -56,6 +83,7 @@ export function connectHOC(WrappedComponent: ComponentClass) {
return html`
<${WrappedComponent}
...${this.props}
active=${this.state.active}
showTooltip=${this.showTooltip}
hideTooltip=${this.hideTooltip}
getBound=${this.getBound}
Expand Down
23 changes: 17 additions & 6 deletions src/ui/components/toolbar/customToolbarItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import {
import { Emitter } from '@t/event';
import html from '@/ui/vdom/template';
import { Component } from '@/ui/vdom/component';
import { getOuterWidth } from '@/utils/dom';
import { cls, getOuterWidth } from '@/utils/dom';
import { createPopupInfo } from '@/ui/toolbarItemFactory';
import { connectHOC } from './buttonHoc';

interface Props {
disabled: boolean;
eventEmitter: Emitter;
item: ToolbarCustomOptions;
active: boolean;
execCommand: ExecCommand;
setPopupInfo: SetPopupInfo;
showTooltip: ShowTooltip;
Expand All @@ -42,6 +43,14 @@ class CustomToolbarItemComp extends Component<Props> {
}
}

updated(prevProps: Props) {
const { item, active, disabled } = this.props;

if (prevProps.active !== active || prevProps.disabled !== disabled) {
item.onUpdated?.({ active, disabled });
}
}

private showTooltip = () => {
this.props.showTooltip(this.refs.el);
};
Expand All @@ -59,16 +68,18 @@ class CustomToolbarItemComp extends Component<Props> {
};

render() {
const style = { display: this.props.item.hidden ? 'none' : 'inline-block' };
const { disabled, item } = this.props;
const style = { display: item.hidden ? 'none' : 'inline-block' };
const getListener = (listener: Function) => (disabled ? null : listener);

return html`
<div
ref=${(el: HTMLElement) => (this.refs.el = el)}
style=${style}
class="tui-toolbar-item-wrapper"
onClick=${this.showPopup}
onMouseover=${this.showTooltip}
onMouseout=${this.props.hideTooltip}
class=${cls('toolbar-item-wrapper')}
onClick=${getListener(this.showPopup)}
onMouseover=${getListener(this.showTooltip)}
onMouseout=${getListener(this.props.hideTooltip)}
></div>
`;
}
Expand Down
32 changes: 4 additions & 28 deletions src/ui/components/toolbar/toolbarButton.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
ExecCommand,
SetPopupInfo,
ToolbarState,
SetItemWidth,
GetBound,
HideTooltip,
Expand All @@ -15,14 +14,11 @@ import { createPopupInfo } from '@/ui/toolbarItemFactory';
import { getOuterWidth } from '@/utils/dom';
import { connectHOC } from './buttonHoc';

interface Payload {
toolbarState: ToolbarState;
}

interface Props {
disabled: boolean;
eventEmitter: Emitter;
item: ToolbarButtonInfo;
active: boolean;
execCommand: ExecCommand;
setPopupInfo: SetPopupInfo;
showTooltip: ShowTooltip;
Expand All @@ -31,29 +27,9 @@ interface Props {
setItemWidth?: SetItemWidth;
}

interface State {
active: boolean;
}

const DEFAULT_WIDTH = 80;

export class ToolbarButtonComp extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { active: false };
this.addEvent();
}

addEvent() {
if (this.props.item.state) {
this.props.eventEmitter.listen('changeToolbarState', ({ toolbarState }: Payload) => {
const active = !!toolbarState[this.props.item.state!];

this.setState({ active });
});
}
}

export class ToolbarButtonComp extends Component<Props> {
mounted() {
this.setItemWidth();
}
Expand Down Expand Up @@ -98,9 +74,9 @@ export class ToolbarButtonComp extends Component<Props, State> {
};

render() {
const { hideTooltip, disabled, item } = this.props;
const { hideTooltip, disabled, item, active } = this.props;
const style = { display: item.hidden ? 'none' : null, ...item.style };
const classNames = `${item.className || ''}${this.state.active ? ' active' : ''}`;
const classNames = `${item.className || ''}${active ? ' active' : ''}`;

return html`
<button
Expand Down
20 changes: 18 additions & 2 deletions src/utils/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,24 @@ export function isPositionInBox(style: CSSStyleDeclaration, offsetX: number, off

const CLS_PREFIX = 'toastui-editor-';

export function cls(...names: string[]) {
return names.map((className) => `${CLS_PREFIX}${className}`).join(' ');
export function cls(...names: (string | [boolean, string])[]) {
const result = [];

for (const name of names) {
let className: string | null;

if (Array.isArray(name)) {
className = name[0] ? name[1] : null;
} else {
className = name;
}

if (className) {
result.push(`${CLS_PREFIX}${className}`);
}
}

return result.join(' ');
}

export function clsWithMdPrefix(...names: string[]) {
Expand Down
2 changes: 1 addition & 1 deletion types/editor.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export interface EditorOptions {
language?: string;
useCommandShortcut?: boolean;
usageStatistics?: boolean;
toolbarItems?: (string | ToolbarItemOptions)[];
toolbarItems?: (string | ToolbarItemOptions)[][];
hideModeSwitch?: boolean;
plugins?: EditorPlugin[];
extendedAutolinks?: ExtendedAutolinks;
Expand Down
9 changes: 8 additions & 1 deletion types/ui.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ export interface ToolbarButtonOptions {
text?: string;
style?: Record<string, any>;
popup?: PopupOptions;
state?: ToolbarStateKeys;
}

interface ToolbarCustomItemState {
active: boolean;
disabled: boolean;
}

export interface ToolbarCustomOptions {
Expand All @@ -20,11 +26,12 @@ export interface ToolbarCustomOptions {
el?: HTMLElement;
popup?: PopupOptions;
hidden?: boolean;
state?: ToolbarStateKeys;
onMounted?: (execCommand: ExecCommand) => void;
onUpdated?: (toolbarState: ToolbarCustomItemState) => void;
}

export type ToolbarButtonInfo = {
state?: ToolbarStateKeys;
hidden?: boolean;
} & ToolbarButtonOptions;

Expand Down

0 comments on commit d72539d

Please sign in to comment.