Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add tooltip element to the builder #246

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions player/react/src/lib/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import * as searchinput from './ui-search-input';
import * as text from './ui-text';
import * as textarea from './ui-text-area';
import * as textinput from './ui-text-input';
import * as tooltip from './ui-definition-tooltip';
import * as link from './ui-link';
import * as loading from './ui-loading';
import * as inlineLoading from './ui-inline-loading';
Expand Down Expand Up @@ -59,6 +60,7 @@ export const allComponents = {
text,
textarea,
textinput,
tooltip,
overflowMenu,
// Tiles
tile,
Expand Down
90 changes: 90 additions & 0 deletions player/react/src/lib/components/ui-definition-tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React from 'react';
import { DefinitionTooltip } from '@carbon/react';
import { CssClasses, SendSignal } from '../types';
import { stringToCssClassName } from '../utils';
import { commonSlots, slotsDisabled } from '../common-slots';

export interface DefinitionTooltipState {
id: string;
type: string;
alignment?: string;
codeContext: {
name: string;
};
description: string;
isDefaultOpened?: boolean;
definition?: string;
isOpenOnHover?: boolean;
cssClasses?: CssClasses[];
style?: any;
}

export const type = 'definition-tooltip';
export const slots = {
...commonSlots,
...slotsDisabled,
enableOpenOnHover: (state: any) => ({
...state,
isOpenOnHover: true
}),
disableOpenOnHover: (state: any) => ({
...state,
isOpenOnHover: false
}),
toggleOpenOnHover: (state: any) => ({
...state,
isOpenOnHover: !state.isOpenOnHover
}),
enableOpenOnDefault: (state: any) => ({
...state,
isDefaultOpened: true
}),
disableOpenOnDefault: (state: any) => ({
...state,
isDefaultOpened: false
}),
toggleOpenOnDefault: (state: any) => ({
...state,
isDefaultOpened: !state.isDefaultOpened
}),
isOpenOnHover: 'boolean',
isDefaultOpened: 'boolean',
alignment: 'string',
definition: 'string',
description: 'string'
};

export const signals = ['click'];

export const UIDefinitionTooltip = ({ state, sendSignal }: {
state: DefinitionTooltipState;
setState: (state: any) => void;
setGlobalState: (state: any) => void;
sendSignal: SendSignal;
}) => {
if (state.type !== 'definition-tooltip') {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <></>;
}

let cssClasses = state.cssClasses?.map((cc: any) => cc.id).join(' ') || '';

if (state.style) {
if (cssClasses.length > 0) {
cssClasses += ' ';
}
cssClasses += stringToCssClassName(state.codeContext.name);
}

return <DefinitionTooltip
id={state.id}
onClick={() => sendSignal(state.id, 'click')}
align={state.alignment ? state.alignment : 'bottom-left'}
definition={state.definition}
name={state.codeContext?.name}
openOnHover={state.isOpenOnHover}
defaultOpen={state.isDefaultOpened}
className={cssClasses}>
{state.description}
</DefinitionTooltip>;
};
4 changes: 4 additions & 0 deletions player/react/src/lib/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { UITile } from './components/ui-tile';
import { UITileFold } from './components/ui-tile-fold';
import { UIToggle } from './components/ui-toggle';
import { kebabCase } from 'lodash';
import { UIDefinitionTooltip } from './components/ui-definition-tooltip';
import { SendSignal } from './types';

export const setItemInState = (item: any, state: any, setState: (state: any) => void) => {
Expand Down Expand Up @@ -211,6 +212,9 @@ export const renderComponents = (
case 'accordion':
return <UIAccordion key={state.id} state={state} sendSignal={sendSignal} setState={setState} setGlobalState={setGlobalState} />;

case 'definition-tooltip':
return <UIDefinitionTooltip key={state.id} state={state} sendSignal={sendSignal} setState={setState} setGlobalState={setGlobalState} />;

case 'accordion-item':
return <UIAccordionItem key={state.id} state={state} sendSignal={sendSignal} setState={setState} setGlobalState={setGlobalState} />;

Expand Down
14 changes: 14 additions & 0 deletions sdk/react/src/lib/assets/component-icons/tooltip.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
214 changes: 214 additions & 0 deletions sdk/react/src/lib/fragment-components/a-definition-tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import React from 'react';
import {
Dropdown,
DefinitionTooltip,
TextInput,
Checkbox
} from '@carbon/react';
import { css } from 'emotion';
import { AComponent, ComponentInfo } from './a-component';

import image from './../assets/component-icons/tooltip.svg';
import {
nameStringToVariableString,
reactClassNamesFromComponentObj
} from '../helpers/tools';
import { styleObjectToString } from '@carbon-builder/player-react';

const preventCheckEvent = css`
pointer-events: none;
`;

export const ADefinitionTooltipSettingsUI = ({ selectedComponent, setComponent }: any) => {
const alignments = [
{ id: 'top', text: 'Top' },
{ id: 'top-left', text: 'Top left' },
{ id: 'top-right', text: 'Top right' },
{ id: 'bottom', text: 'Bottom' },
{ id: 'bottom-left', text: 'Bottom left' },
{ id: 'bottom-right', text: 'Bottom right' },
{ id: 'left', text: 'Left' },
{ id: 'left-bottom', text: 'Left bottom' },
{ id: 'left-top', text: 'Left top' },
{ id: 'right', text: 'Right' },
{ id: 'right-bottom', text: 'Right bottom' },
{ id: 'right-top', text: 'Right top' }
];

return <>
<Dropdown
id='alignment'
label='Alignment of text'
titleText='Alignment'
items={alignments}
selectedItem={alignments.find(item => item.id === selectedComponent.alignment)}
itemToString={(item: any) => (item ? item.text : '')}
onChange={(event: any) => setComponent({
...selectedComponent,
alignment: event.selectedItem.id
})} />
<TextInput
id='description'
value={selectedComponent.description}
labelText='Description'
onChange={(event: any) => setComponent({
...selectedComponent,
description: event.currentTarget.value
})} />
<TextInput
id='definition'
value={selectedComponent.definition}
labelText='Tooltip Message'
onChange={(event: any) => setComponent({
...selectedComponent,
definition: event.currentTarget.value
})} />
<Checkbox
id='defaultOpen'
labelText='Default open'
checked={selectedComponent.isDefaultOpened}
onChange={(_: any, { checked }: any) => setComponent({
...selectedComponent,
isDefaultOpened: checked
})} />
<Checkbox
id='openOnHover'
labelText='Open on hover'
checked={selectedComponent.isOpenOnHover}
onChange={(_: any, { checked }: any) => setComponent({
...selectedComponent,
isOpenOnHover: checked
})} />
</>;
};

export const ADefinitionTooltipCodeUI = ({ selectedComponent, setComponent }: any) => <TextInput
value={selectedComponent.codeContext?.name}
id='input-name'
labelText='Input name'
onChange={(event: any) => {
setComponent({
...selectedComponent,
codeContext: {
...selectedComponent.codeContext,
name: event.currentTarget.value
}
});
}} />;

export const ADefinitionTooltip = ({
componentObj,
...rest
}: any) => {
return (
<AComponent
componentObj={componentObj}
rejectDrop={true}
{...rest}>
<div className={`${preventCheckEvent} ${componentObj.cssClasses?.map((cc: any) => cc.id).join(' ')} `}>
<DefinitionTooltip
className={css`${styleObjectToString(componentObj.style)}`}
definition={componentObj.definition}
align={componentObj.alignment}>
{componentObj.description}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In both frameworks description is a child, so why don't we do that here too? Meaning description wouldn't be part of the componentObj.description - instead we just let items render.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we stick with support only text only for now

</DefinitionTooltip>
</div>
</AComponent>
);
};

export const componentInfo: ComponentInfo = {
component: ADefinitionTooltip,
settingsUI: ADefinitionTooltipSettingsUI,
codeUI: ADefinitionTooltipCodeUI,
keywords: ['definition tooltip', 'definition', 'tooltip'],
name: 'Definition tooltip',
type: 'definition-tooltip',
defaultComponentObj: {
type: 'definition-tooltip',
description: 'sample text'
},
image,
codeExport: {
angular: {
latest: {
inputs: ({ json }) => `
@Input() ${nameStringToVariableString(json.codeContext?.name)}Description = "${json.description ? json.description : 'description'}";
@Input() ${nameStringToVariableString(json.codeContext?.name)}IsOpen = ${json.isDefaultOpened ? json.isDefaultOpened : false};
@Input() ${nameStringToVariableString(json.codeContext?.name)}Align: any = "${json.alignment ? json.alignment : 'bottom-start' }";
@Input() ${nameStringToVariableString(json.codeContext?.name)}Definition = "${json.definition ?
json.definition : 'default tooltip message' }";`,
outputs: ({ json }) => {
const name = nameStringToVariableString(json.codeContext?.name);
return `@Output() ${name}IsOpenChange = new EventEmitter<any>();
@Output() ${name}OnClose = new EventEmitter<any>();
@Output() ${name}OnOpen = new EventEmitter<any>();`;
},
imports: ['TooltipModule'],
code: ({ json }) => {
const name = nameStringToVariableString(json.codeContext?.name);
return `<cds-tooltip-definition
[isOpen]="${name}IsOpen"
[align]="${name}Align"
(onOpen)="${name}OnOpen.emit($event)"
(onClose)="${name}OnClose.emit($event)"
(isOpenChange)="${name}IsOpenChange.emit($event)"
[description]="${name}Definition">
{{${name}Description}}
</cds-tooltip-definition>`;
}
},
v10: {
inputs: ({ json }) => `
@Input() ${nameStringToVariableString(json.codeContext?.name)}Description = "${json.description ? json.description : 'description'}";
@Input() ${nameStringToVariableString(json.codeContext?.name)}IsOpen = ${json.isDefaultOpened ? json.isDefaultOpened : false};
@Input() ${nameStringToVariableString(json.codeContext?.name)}Align: any = "${json.alignment ? json.alignment : 'bottom-left' }";
@Input() ${nameStringToVariableString(json.codeContext?.name)}Definition = "${json.definition ?
json.definition : 'default tooltip message' }";`,
outputs: ({ json }) => {
const name = nameStringToVariableString(json.codeContext?.name);
return `@Output() ${name}IsOpenChange = new EventEmitter<any>();
@Output() ${name}OnClose = new EventEmitter<any>();
@Output() ${name}OnOpen = new EventEmitter<any>();`;
},
imports: ['TooltipModule'],
code: ({ json }) => {
const name = nameStringToVariableString(json.codeContext?.name);
return `<ibm-tooltip-definition
[isOpen]="${name}IsOpen"
[align]="${name}Align"
(onOpen)="${name}OnOpen.emit($event)"
(onClose)="${name}OnClose.emit($event)"
(isOpenChange)="${name}IsOpenChange.emit($event)"
[description]="${name}Definition">
{{${name}Description}}
</ibm-tooltip-definition>`;
}
}
},
react: {
latest: {
imports: ['DefinitionTooltip'],
code: ({ json }) => `<DefinitionTooltip
${reactClassNamesFromComponentObj(json)}
align="${json.alignment}"
${json.isDefaultOpened ? `defaultOpen={${json.isDefaultOpened}}` : ''}
definition="${json.definition ? json.definition : 'default tooltip message'}"
${json.isOpenOnHover ? `openOnHover={${json.isOpenOnHover}}` : ''}>
${json.description ? json.description : 'description'}
</DefinitionTooltip>`
},
v10: {
imports: ['DefinitionTooltip'],
code: ({ json }) => `<DefinitionTooltip
${reactClassNamesFromComponentObj(json)}
align="${json.alignment}"
${json.isDefaultOpened ? `defaultOpen={${json.isDefaultOpened}}` : ''}
definition="${json.definition ? json.definition : 'default tooltip message'}"
${json.isOpenOnHover ? `openOnHover={${json.isOpenOnHover}}` : ''}>
${json.description ? json.description : 'description'}
</DefinitionTooltip>`
}
}
}
};
3 changes: 3 additions & 0 deletions sdk/react/src/lib/fragment-components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import * as link from './a-link';
import * as loading from './a-loading';
import * as inlineLoading from './a-inline-loading';
import * as overflowMenu from './a-overflow-menu';
import * as definitionTooltip from './a-definition-tooltip';
// Tiles
import * as tile from './tiles/a-tile';
import * as toggle from './a-toggle';
Expand Down Expand Up @@ -63,6 +64,7 @@ export { ATextInput, ATextInputSettingsUI, ATextInputCodeUI } from './a-text-inp
export { AOverflowMenu, AOverflowMenuCodeUI, AOverflowMenuSettingsUI } from './a-overflow-menu';
export { ARadio, ARadioSettingsUI, ARadioCodeUI } from './a-radio';
export { ARadioGroup, ARadioGroupSettingsUI, ARadioGroupCodeUI } from './a-radio-group';
export { ADefinitionTooltip, ADefinitionTooltipSettingsUI, ADefinitionTooltipCodeUI } from './a-definition-tooltip';

// Tiles
export { ATile, ATileCodeUI, ATileSettingsUI } from './tiles/a-tile';
Expand Down Expand Up @@ -102,6 +104,7 @@ export const allComponents = {
textarea,
textinput,
overflowMenu,
definitionTooltip,
// Tiles
tile,
toggle,
Expand Down
Loading