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

Commit

Permalink
Add tooltip to icon component
Browse files Browse the repository at this point in the history
Signed-off-by: Sam Foo <foos@vmware.com>
  • Loading branch information
Sam Foo committed Apr 21, 2021
1 parent d91a8d3 commit d959d50
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 27 deletions.
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,17 @@ export class IconComponent extends AbstractViewComponent<IconView> {

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

if (this.tooltip) {
this.tooltipClass = 'tooltip';
if (this.tooltip.size !== '') {
this.tooltipClass += ' tooltip-' + this.tooltip.size;
}
if (this.tooltip.position !== '') {
this.tooltipClass += ' tooltip-' + this.tooltip.position;
}
}

this.shape = view.config.shape;
this.flip = view.config.flip;
Expand Down
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} />

0 comments on commit d959d50

Please sign in to comment.