Skip to content
This repository has been archived by the owner on Jan 19, 2023. It is now read-only.

Add tooltip to icon component #2350

Merged
merged 1 commit into from
Apr 22, 2021
Merged
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
1 change: 1 addition & 0 deletions changelogs/unreleased/2350-GuessWhoSamFoo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added tooltips for icons
62 changes: 51 additions & 11 deletions pkg/view/component/icon.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ type Icon struct {
}

type IconConfig struct {
Shape string `json:"shape"`
Size string `json:"size"`
Direction Direction `json:"direction"`
Flip Flip `json:"flip"`
Solid bool `json:"solid"`
Status Status `json:"status"`
Inverse bool `json:"inverse"`
Badge Badge `json:"badge"`
Color string `json:"color"`
BadgeColor string `json:"badgeColor"`
Label string `json:"label"`
Shape string `json:"shape"`
Size string `json:"size"`
Direction Direction `json:"direction"`
Flip Flip `json:"flip"`
Solid bool `json:"solid"`
Status Status `json:"status"`
Inverse bool `json:"inverse"`
Badge Badge `json:"badge"`
Color string `json:"color"`
BadgeColor string `json:"badgeColor"`
Label string `json:"label"`
Tooltip *TooltipConfig `json:"tooltip,omitempty"`
}

type Direction string
Expand Down Expand Up @@ -60,6 +61,34 @@ const (
BadgeInheritTriangle Badge = "inherit-triangle"
)

type IconOption func(icon *Icon)

type TooltipConfig struct {
Message string `json:"message"`
Size TooltipSize `json:"size"`
Position TooltipPosition `json:"position"`
}

type TooltipSize string

const (
TooltipExtraSmall TooltipSize = "xs"
TooltipSmall TooltipSize = "sm"
TooltipMedium TooltipSize = "md"
TooltipLarge TooltipSize = "lg"
)

type TooltipPosition string

const (
TooltipLeft TooltipPosition = "left"
TooltipRight TooltipPosition = "right"
TooltipTopLeft TooltipPosition = "top-left"
TooltipTopRight TooltipPosition = "top-right"
TooltipBottomLeft TooltipPosition = "bottom-left"
TooltipBottomRight TooltipPosition = "bottom-right"
)

func NewIcon(shape string, options ...func(*Icon)) *Icon {
i := &Icon{
Base: newBase(TypeIcon, nil),
Expand Down Expand Up @@ -87,6 +116,17 @@ func (i *Icon) MarshalJSON() ([]byte, error) {
return json.Marshal(&m)
}

// WithTooltip configures an icon with a tooltip
func WithTooltip(message string, size TooltipSize, position TooltipPosition) IconOption {
return func(icon *Icon) {
icon.Config.Tooltip = &TooltipConfig{
Message: message,
Size: size,
Position: position,
}
}
}

// AddLabel adds an aria-label for screen readers
func (i *Icon) AddLabel(label string) {
i.Config.Label = label
Expand Down
32 changes: 31 additions & 1 deletion pkg/view/component/icon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
func Test_Icon_Marshal(t *testing.T) {
test := []struct {
name string
input Component
input *Icon
expectedPath string
isErr bool
}{
Expand Down Expand Up @@ -55,3 +55,33 @@ func Test_Icon_Marshal(t *testing.T) {
})
}
}

func Test_Icon_Options(t *testing.T) {
test := []struct {
name string
optsFunc IconOption
expected *Icon
}{
{
name: "Icon with tooltip",
optsFunc: WithTooltip("hello", TooltipLarge, TooltipTopRight),
expected: &Icon{
Base: newBase(TypeIcon, nil),
Config: IconConfig{
Tooltip: &TooltipConfig{
Message: "hello",
Size: TooltipLarge,
Position: TooltipTopRight,
},
},
},
},
}

for _, tc := range test {
t.Run(tc.name, func(t *testing.T) {
result := NewIcon("", tc.optsFunc)
assert.Equal(t, tc.expected, result)
})
}
}
2 changes: 1 addition & 1 deletion pkg/view/component/signpost.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func NewSignpost(t Component, m string) *Signpost {
return so
}

// SetStatus sets the status of the text component.
// SetPosition sets the status of the text component.
func (t *Signpost) SetPosition(position Position) {
t.Config.Position = position
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class ButtonComponent extends AbstractViewComponent<ButtonView> {
}

update() {
let button = this.v.config;
const button = this.v.config;
if (button.modal) {
this.modalView = this.v.config.modal;
const modal = this.modalView as ModalView;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
<cds-icon
[style]="iconStyle != '' ? iconStyle : null"
[attr.shape]="shape"
[attr.size]="size"
[attr.direction]="direction"
[attr.flip]="flip"
[attr.solid]="isSolid ? true: null"
[attr.status]="status"
[attr.inverse]="isInverse ? true: null"
[attr.badge]="badge"
[attr.aria-label]="label"
></cds-icon>
<ng-container *ngIf="!tooltip; else hasTooltip">
<cds-icon
[style]="iconStyle != '' ? iconStyle : null"
[attr.shape]="shape"
[attr.size]="size"
[attr.direction]="direction"
[attr.flip]="flip"
[attr.solid]="isSolid ? true: null"
[attr.status]="status"
[attr.inverse]="isInverse ? true: null"
[attr.badge]="badge"
[attr.aria-label]="label"
></cds-icon>
</ng-container>

<ng-template #hasTooltip>
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="{{ tooltipClass }}">
<cds-icon
[style]="iconStyle != '' ? iconStyle : null"
[attr.shape]="shape"
[attr.size]="size"
[attr.direction]="direction"
[attr.flip]="flip"
[attr.solid]="isSolid ? true: null"
[attr.status]="status"
[attr.inverse]="isInverse ? true: null"
[attr.badge]="badge"
[attr.aria-label]="label"
></cds-icon>
<span class="tooltip-content">{{ tooltip.message }}</span>
</a>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
loadTechnologyIconSet,
loadChartIconSet,
} from '@cds/core/icon';
import { IconView } from '../../../models/content';
import { IconView, Tooltip } from '../../../models/content';

@Component({
selector: 'app-view-icon',
Expand All @@ -35,6 +35,8 @@ export class IconComponent extends AbstractViewComponent<IconView> {
status: string;
iconStyle: string;
label: string;
tooltip: Tooltip;
tooltipClass: string;

constructor(private cdr: ChangeDetectorRef) {
super();
Expand All @@ -51,6 +53,11 @@ export class IconComponent extends AbstractViewComponent<IconView> {

protected update(): void {
const view = this.v;
this.tooltip = view.config.tooltip;
Copy link
Contributor

Choose a reason for hiding this comment

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

This function is already long it will be nice to have this functionality in another function


if (this.tooltip) {
this.generateTooltipClassStyles();
}

this.shape = view.config.shape;
this.flip = view.config.flip;
Expand All @@ -72,4 +79,15 @@ export class IconComponent extends AbstractViewComponent<IconView> {
this.label = view.config.label;
this.cdr.markForCheck();
}

generateTooltipClassStyles() {
this.tooltipClass = 'tooltip';
if (this.tooltip.size !== '') {
this.tooltipClass += ' tooltip-' + this.tooltip.size;
}
if (this.tooltip.position !== '') {
this.tooltipClass += ' tooltip-' + this.tooltip.position;
}
return this.tooltipClass;
}
}
7 changes: 7 additions & 0 deletions web/src/app/modules/shared/models/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ export interface IconView extends View {
color?: string;
badgeColor?: string;
label?: string;
tooltip?: Tooltip;
};
}

Expand All @@ -605,6 +606,12 @@ export interface SignpostView extends View {
};
}

export interface Tooltip {
message: string;
size: string;
position: string;
}

export interface ButtonView extends View {
config: {
payload: {};
Expand Down
40 changes: 40 additions & 0 deletions web/src/stories/icon.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ c.Config.Badge = component.BadgeDanger
c.Config.AddLabel("label for accessibility")
`}}

export const iconWithTooltipDocs = {source: {code: `
component.NewIcon("user", component.WithTooltip("hello", component.TooltipMedium, component.TooltipRight)
`}}

<h1>Icon Component</h1>
<h2>Description</h2>
<p>An Icon component is use to display icons use the API from <a href="https://clarity.design/foundation/icons/api/#icon-properties"> Clarity 5 </a></p>
Expand Down Expand Up @@ -47,5 +51,41 @@ c.Config.AddLabel("label for accessibility")
</Story>
</Canvas>

<h2>Icon with tooltip</h2>
<p>An icon can be configured with a tooltip to display a message on hover.</p>

<Canvas withToolbar>
<Story name="Icon with tooltip" parameters={{ docs: iconWithTooltipDocs }} height="200px" >
{{
props: {
view: object('View', {
metadata: {
type: 'icon',
},
config: {
shape: "user",
size: "",
direction: "",
flip: "",
solid: false,
status: "",
inverse: false,
badge: "",
color: "",
badgeColor: "",
label: "",
tooltip: {
message: "hello",
size: "md",
position: "right",
}
}
}),
},
component: IconComponent,
}}
</Story>
</Canvas>

<h2>Props</h2>
<ArgsTable of={IconComponent} />