Skip to content

Commit 4916c70

Browse files
committed
Merge branch 'improvement/helpIcon-style-issue' into q/1.0
2 parents f31a8c0 + e009220 commit 4916c70

3 files changed

Lines changed: 212 additions & 3 deletions

File tree

src/lib/components/iconhelper/IconHelper.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CSSProperties, ReactNode } from 'react';
22
import styled from 'styled-components';
33
import { Icon } from '../icon/Icon.component';
4+
import { fontSize } from '../../style/theme';
45
import { Position, Tooltip } from '../tooltip/Tooltip.component';
56

67
type IconHelpProps = {
@@ -19,14 +20,18 @@ type IconHelpProps = {
1920

2021
const HelpButton = styled.button`
2122
display: inline-flex;
23+
align-items: center;
24+
justify-content: center;
25+
width: ${fontSize.base};
26+
height: ${fontSize.base};
2227
background: none;
2328
border: none;
2429
padding: 0;
2530
margin: 0;
2631
color: inherit;
27-
font: inherit; /* Inherit font sizing */
28-
vertical-align: text-bottom; /* Align with text */
29-
line-height: 1;
32+
font-size: ${fontSize.base};
33+
line-height: 0;
34+
vertical-align: -0.125em;
3035
cursor: default;
3136
&:focus-visible {
3237
outline: 2px dashed ${(props) => props.theme.selectedActive};
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Meta, Canvas } from '@storybook/addon-docs/blocks';
2+
import * as Stories from './iconhelp.stories';
3+
4+
<Meta name="Guideline" of={Stories} />
5+
6+
# IconHelp
7+
8+
`IconHelp` is an inline icon button that reveals a tooltip on hover or focus.
9+
10+
<Canvas of={Stories.Simple} sourceState="none" />
11+
12+
## When to use it
13+
14+
Use `IconHelp` to provide secondary information without overloading the interface. Reach for it whenever a user might have to **guess** what a label, value or concept means — if the wording is not self-explanatory and there is no room (or no need) to spell it out in the layout, `IconHelp` should be considered.
15+
16+
Typical cases:
17+
18+
- A label or a settings name that uses a domain term (`Object lock`, `Replication policy`, `RPO`).
19+
- A value whose meaning is not transparent (`Governance`, `Compliance`, `Standard` storage class).
20+
- A column header, badge, or status that benefits from a one-line definition.
21+
22+
## When not to use it
23+
24+
- **Don't use it as the only way to convey critical information.** A tooltip is hidden by default — anything required to complete a task must remain visible (helper text, error message, inline description).
25+
- **Don't stack multiple `IconHelp` next to each other.** If a section needs that much explanation, surface it directly: add a description paragraph under the section heading, or place an `InfoMessage` above the content area. For longer explanations, link to the dedicated documentation page from that `InfoMessage` — never put long text in a tooltip.
26+
- **Don't replace good labels with an `IconHelp`.** Fix the label first; add help only when the term itself cannot be made clearer.
27+
- **Don't put long content inside the tooltip.** Keep it to one or two short sentences. For longer explanations, use a dedicated documentation page.
28+
29+
## Explaining keys and values in a key/value list
30+
31+
`IconHelp` can sit on either side of a key/value pair:
32+
33+
- On the **key** when the label itself is the opaque term (`Object lock`, `RPO`, `Replication policy`).
34+
- On the **value** when the label is clear but the value is the unclear term (`Governance`, `Standard` as a storage class).
35+
36+
Leave plain entries (a name, an email, an ID, a region code) without one.
37+
38+
<Canvas of={Stories.InKeyValueList} sourceState="none" />
39+
40+
## In a form
41+
42+
Use `IconHelp` (via `FormGroup`'s `labelHelpTooltip`) only for **conceptual context** — what a field represents and why it matters. Anything the user needs to complete the field correctly (format, validation rule, required input) belongs in **helper text**, surfaced via `FormGroup`'s `help` prop, because helper text is always visible.
43+
44+
Good uses of `labelHelpTooltip`:
45+
46+
- **What the field represents** — e.g. _"The name other users will see on your profile."_
47+
- **The impact of the value** — e.g. _"Enabling versioning keeps a copy of every overwrite or delete."_
48+
49+
Format and validation hints should never live in a tooltip.
50+
51+
## Placement
52+
53+
The tooltip defaults to `right`. Pick another placement only when `right` is clipped by the container or overlaps important content.
54+
55+
<Canvas of={Stories.WithPlacement} sourceState="none" />
56+
57+
## Accessibility
58+
59+
- Always pass an `aria-label` that describes _what the tooltip explains_, not just `"Help"`. Example: `aria-label="More info about replication policy"`.
60+
- The icon renders as a real `<button>`, so it is reachable by keyboard (`Tab`) and the tooltip opens on focus.
61+
- The tooltip content is the visible text — keep it informative and complete on its own.
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import React from 'react';
2+
import { IconHelp } from '../../src/lib/components/iconhelper/IconHelper';
3+
import { Text } from '../../src/lib/components/text/Text.component';
4+
import { Stack } from '../../src/lib/spacing';
5+
import { Wrapper } from '../common';
6+
7+
export default {
8+
title: 'Components/IconHelp',
9+
component: IconHelp,
10+
decorators: [(story: () => React.ReactNode) => <Wrapper>{story()}</Wrapper>],
11+
};
12+
13+
export const Simple = {
14+
name: 'Simple',
15+
render: () => (
16+
<Stack direction="horizontal" gap="r4">
17+
<Text>Replication policy</Text>
18+
<IconHelp
19+
tooltipMessage="Defines how objects are replicated across locations."
20+
aria-label="More info about replication policy"
21+
/>
22+
</Stack>
23+
),
24+
};
25+
26+
export const WithPlacement = {
27+
name: 'Tooltip placement',
28+
render: () => (
29+
<Stack direction="vertical" gap="r24">
30+
<Stack direction="horizontal" gap="r4">
31+
<Text>
32+
Placement <b>top</b>
33+
</Text>
34+
<IconHelp
35+
placement="top"
36+
tooltipMessage="Appears above the icon."
37+
aria-label="Top tooltip"
38+
/>
39+
</Stack>
40+
<Stack direction="horizontal" gap="r4">
41+
<Text>
42+
Placement <b>right</b> (default)
43+
</Text>
44+
<IconHelp
45+
placement="right"
46+
tooltipMessage="Appears to the right of the icon."
47+
aria-label="Right tooltip"
48+
/>
49+
</Stack>
50+
<Stack direction="horizontal" gap="r4">
51+
<Text>
52+
Placement <b>bottom</b>
53+
</Text>
54+
<IconHelp
55+
placement="bottom"
56+
tooltipMessage="Appears below the icon."
57+
aria-label="Bottom tooltip"
58+
/>
59+
</Stack>
60+
<Stack direction="horizontal" gap="r4">
61+
<Text>
62+
Placement <b>left</b>
63+
</Text>
64+
<IconHelp
65+
placement="left"
66+
tooltipMessage="Appears to the left of the icon."
67+
aria-label="Left tooltip"
68+
/>
69+
</Stack>
70+
</Stack>
71+
),
72+
};
73+
74+
const KeyValueRow = ({
75+
label,
76+
labelHelp,
77+
labelAriaLabel,
78+
value,
79+
helpMessage,
80+
helpAriaLabel,
81+
}: {
82+
label: string;
83+
labelHelp?: React.ReactNode;
84+
labelAriaLabel?: string;
85+
value: React.ReactNode;
86+
helpMessage?: React.ReactNode;
87+
helpAriaLabel?: string;
88+
}) => (
89+
<div
90+
style={{
91+
display: 'grid',
92+
gridTemplateColumns: '220px 1fr',
93+
alignItems: 'baseline',
94+
gap: '16px',
95+
padding: '6px 0',
96+
}}
97+
>
98+
<Stack direction="horizontal" gap="r4">
99+
<Text color="textSecondary">{label}</Text>
100+
{labelHelp && (
101+
<IconHelp
102+
tooltipMessage={labelHelp}
103+
aria-label={labelAriaLabel ?? `More info about ${label}`}
104+
/>
105+
)}
106+
</Stack>
107+
<Stack direction="horizontal" gap="r4">
108+
<Text>{value}</Text>
109+
{helpMessage && (
110+
<IconHelp
111+
tooltipMessage={helpMessage}
112+
aria-label={helpAriaLabel ?? `More info about ${label} value`}
113+
/>
114+
)}
115+
</Stack>
116+
</div>
117+
);
118+
119+
export const InKeyValueList = {
120+
name: 'Explaining keys and values',
121+
render: () => (
122+
<div style={{ maxWidth: 620 }}>
123+
<KeyValueRow label="Bucket name" value="prod-customer-assets" />
124+
<KeyValueRow
125+
label="Object lock"
126+
labelHelp="Object lock prevents objects from being deleted or overwritten for a fixed amount of time."
127+
value="Governance"
128+
helpMessage="Governance mode allows privileged users to change retention; Compliance mode does not."
129+
/>
130+
<KeyValueRow
131+
label="RPO"
132+
labelHelp="Recovery Point Objective — the maximum acceptable amount of data loss measured in time."
133+
value="15 minutes"
134+
/>
135+
<KeyValueRow
136+
label="Storage class"
137+
value="Standard"
138+
helpMessage="Affects durability, latency and pricing of stored objects."
139+
/>
140+
<KeyValueRow label="Region" value="us-east-1" />
141+
</div>
142+
),
143+
};

0 commit comments

Comments
 (0)