@transferwise/components 0.0.0-experimental-a41b256 → 0.0.0-experimental-840bf87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/header/Header.js +38 -78
- package/build/header/Header.js.map +1 -1
- package/build/header/Header.mjs +38 -75
- package/build/header/Header.mjs.map +1 -1
- package/build/index.js +1 -1
- package/build/index.mjs +1 -1
- package/build/inputs/SelectInput.js +1 -1
- package/build/inputs/SelectInput.js.map +1 -1
- package/build/inputs/SelectInput.mjs +1 -1
- package/build/main.css +11 -17
- package/build/selectOption/SelectOption.js +1 -1
- package/build/selectOption/SelectOption.js.map +1 -1
- package/build/selectOption/SelectOption.mjs +1 -1
- package/build/styles/header/Header.css +11 -17
- package/build/styles/main.css +11 -17
- package/build/title/Title.js +4 -6
- package/build/title/Title.js.map +1 -1
- package/build/title/Title.mjs +4 -6
- package/build/title/Title.mjs.map +1 -1
- package/build/types/header/Header.d.ts +11 -28
- package/build/types/header/Header.d.ts.map +1 -1
- package/build/types/header/index.d.ts +0 -1
- package/build/types/header/index.d.ts.map +1 -1
- package/build/types/index.d.ts +0 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/title/Title.d.ts +4 -3
- package/build/types/title/Title.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/header/Header.css +11 -17
- package/src/header/Header.less +8 -17
- package/src/header/Header.spec.tsx +50 -68
- package/src/header/Header.story.tsx +39 -184
- package/src/header/Header.tsx +69 -122
- package/src/header/index.ts +0 -1
- package/src/index.ts +0 -1
- package/src/main.css +11 -17
- package/src/title/Title.tsx +11 -25
- package/src/header/Header.accessibility.docs.mdx +0 -85
|
@@ -1,198 +1,53 @@
|
|
|
1
|
-
import
|
|
2
|
-
import Header, { HeaderProps } from './Header';
|
|
3
|
-
import { storyConfig } from '../test-utils';
|
|
1
|
+
import Header from './Header';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Not all stories need access to all controls as it causes unnecessary UI noise.
|
|
13
|
-
*/
|
|
14
|
-
const hideControls = (args: string[]) =>
|
|
15
|
-
Object.fromEntries(args.map((item) => [item, { table: { disable: true } }]));
|
|
3
|
+
export default {
|
|
4
|
+
component: Header,
|
|
5
|
+
title: 'Typography/Header',
|
|
6
|
+
};
|
|
16
7
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*/
|
|
20
|
-
const renderHeader = (args: HeaderProps) => {
|
|
21
|
-
if (args.as === 'legend') {
|
|
22
|
-
return (
|
|
23
|
-
<fieldset style={{ width: '100%' }}>
|
|
24
|
-
<Header {...args} />
|
|
25
|
-
</fieldset>
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
return <Header {...args} />;
|
|
8
|
+
export const Basic = () => {
|
|
9
|
+
return <Header title="Header title" />;
|
|
29
10
|
};
|
|
30
11
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*/
|
|
34
|
-
const renderVariants = () => (
|
|
35
|
-
<div
|
|
36
|
-
className="header-variants"
|
|
37
|
-
style={{ display: 'flex', flexWrap: 'wrap', gap: '16px', maxWidth: '1200px' }}
|
|
38
|
-
>
|
|
39
|
-
{(['h1', 'h2', 'h3', 'legend'] as const).map((as) => (
|
|
40
|
-
<Header key={as} as={as} title={`Header as ${as}`} />
|
|
41
|
-
))}
|
|
42
|
-
{(['section', 'group'] as const).map((level) => (
|
|
43
|
-
<Header key={level} level={level} title={`Header level ${level}`} />
|
|
44
|
-
))}
|
|
12
|
+
export const WithAction = () => {
|
|
13
|
+
return (
|
|
45
14
|
<Header
|
|
46
|
-
|
|
47
|
-
title="Header with Action"
|
|
15
|
+
title="Header title"
|
|
48
16
|
action={{
|
|
49
|
-
'aria-label': '
|
|
50
|
-
text: '
|
|
51
|
-
onClick: () => alert('Action
|
|
17
|
+
'aria-label': 'Magic',
|
|
18
|
+
text: 'Click me!',
|
|
19
|
+
onClick: () => alert('Action!'),
|
|
52
20
|
}}
|
|
53
21
|
/>
|
|
54
|
-
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* The stories below document the `Header` component, which is used to structure content and convey hierarchy. <br />
|
|
59
|
-
* For more details, refer to the [design documentation](https://wise.design/components/header).
|
|
60
|
-
*/
|
|
61
|
-
const meta: Meta<typeof Header> = {
|
|
62
|
-
component: Header,
|
|
63
|
-
title: 'Typography/Header',
|
|
64
|
-
argTypes: {
|
|
65
|
-
level: {
|
|
66
|
-
type: {
|
|
67
|
-
name: 'enum',
|
|
68
|
-
value: ['section', 'group'],
|
|
69
|
-
},
|
|
70
|
-
table: {
|
|
71
|
-
type: {
|
|
72
|
-
summary: 'HeaderLevel',
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
description: 'Defines the hierarchical level of the header.',
|
|
76
|
-
},
|
|
77
|
-
as: {
|
|
78
|
-
type: {
|
|
79
|
-
name: 'enum',
|
|
80
|
-
value: ['h1', 'h2', 'h3', 'legend'],
|
|
81
|
-
},
|
|
82
|
-
table: {
|
|
83
|
-
type: {
|
|
84
|
-
summary: 'HeaderAs',
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
description:
|
|
88
|
-
'Defines which HTML element the Header will render as. Use `legend` only as the first child of a `<fieldset>` — otherwise a warning will appear. The `action` prop is not supported with `legend`. You can also use heading tags like `h1`, `h2`, `h3`, or `header`.',
|
|
89
|
-
},
|
|
90
|
-
action: {
|
|
91
|
-
table: {
|
|
92
|
-
type: {
|
|
93
|
-
summary: 'HeaderAction',
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
description: 'Defines an optional action (e.g., button or link) associated with the header.',
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
args: {
|
|
100
|
-
title: 'Default Header',
|
|
101
|
-
level: 'group',
|
|
102
|
-
as: 'h1',
|
|
103
|
-
action: undefined,
|
|
104
|
-
},
|
|
105
|
-
tags: ['autodocs'],
|
|
106
|
-
decorators: [withContainer],
|
|
107
|
-
parameters: {
|
|
108
|
-
docs: {
|
|
109
|
-
toc: true,
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
export default meta;
|
|
115
|
-
|
|
116
|
-
type Story = StoryObj<typeof Header>;
|
|
117
|
-
|
|
118
|
-
export const Playground: Story = {
|
|
119
|
-
render: renderHeader,
|
|
120
|
-
args: {
|
|
121
|
-
title: 'Playground Header',
|
|
122
|
-
level: 'group',
|
|
123
|
-
as: 'h2',
|
|
124
|
-
action: {
|
|
125
|
-
'aria-label': 'Action',
|
|
126
|
-
text: 'Click me',
|
|
127
|
-
onClick: () => alert('Action clicked!'),
|
|
128
|
-
},
|
|
129
|
-
},
|
|
22
|
+
);
|
|
130
23
|
};
|
|
131
24
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
argTypes: hideControls(['as', 'className', 'level', 'title', 'testId']),
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Demonstrates a `Header` with an associated action as a link.
|
|
149
|
-
*/
|
|
150
|
-
export const ActionAsLink: Story = {
|
|
151
|
-
args: {
|
|
152
|
-
title: 'Header with Action as Link',
|
|
153
|
-
action: {
|
|
154
|
-
'aria-label': 'Learn more about this section',
|
|
155
|
-
text: 'Learn more',
|
|
156
|
-
href: 'https://wise.com',
|
|
157
|
-
target: '_blank',
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
argTypes: hideControls(['as', 'className', 'level', 'title', 'testId']),
|
|
25
|
+
export const WithActionAsLink = () => {
|
|
26
|
+
return (
|
|
27
|
+
<Header
|
|
28
|
+
title="Header title"
|
|
29
|
+
action={{
|
|
30
|
+
text: 'This is a link',
|
|
31
|
+
href: 'https://wise.com',
|
|
32
|
+
}}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
161
35
|
};
|
|
162
36
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
argTypes: hideControls(['action', 'className', 'level', 'testId']),
|
|
173
|
-
};
|
|
37
|
+
export const WithActionAsLinkPreventingNavigationWithTracking = () => {
|
|
38
|
+
return (
|
|
39
|
+
<Header
|
|
40
|
+
title="Header title"
|
|
41
|
+
action={{
|
|
42
|
+
text: 'This is a link',
|
|
43
|
+
href: 'https://wise.com',
|
|
44
|
+
onClick: (event: React.MouseEvent<HTMLElement>) => {
|
|
45
|
+
alert('Running onClick handler');
|
|
174
46
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
level: 'section',
|
|
182
|
-
},
|
|
183
|
-
argTypes: hideControls(['action', 'as', 'className', 'title', 'testId']),
|
|
47
|
+
// we can stop the navigation from happening as onClick will always run before href redirect
|
|
48
|
+
event.preventDefault();
|
|
49
|
+
},
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
184
53
|
};
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Displays all variants of the `Header` component, including different levels, tags, and actions.
|
|
188
|
-
*/
|
|
189
|
-
export const AllVariants = storyConfig(
|
|
190
|
-
{
|
|
191
|
-
tags: ['!autodocs'],
|
|
192
|
-
parameters: {
|
|
193
|
-
padding: '0',
|
|
194
|
-
},
|
|
195
|
-
render: renderVariants,
|
|
196
|
-
},
|
|
197
|
-
{ variants: ['default', 'dark', 'bright-green', 'forest-green', 'rtl'] },
|
|
198
|
-
);
|
package/src/header/Header.tsx
CHANGED
|
@@ -1,155 +1,102 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
|
|
3
|
+
import { ActionButtonProps } from '../actionButton/ActionButton';
|
|
4
|
+
import Button from '../button';
|
|
3
5
|
import { AriaLabelProperty, CommonProps, Heading, LinkProps, Typography } from '../common';
|
|
4
6
|
import Link from '../link';
|
|
5
|
-
import Button from '../button';
|
|
6
7
|
import Title from '../title';
|
|
7
|
-
import
|
|
8
|
+
import { HTMLAttributes } from 'react';
|
|
8
9
|
|
|
9
10
|
type ActionProps = AriaLabelProperty & {
|
|
10
11
|
text: string;
|
|
11
|
-
onClick?: () => void;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
type ButtonActionProps = ActionProps;
|
|
14
|
+
type ButtonActionProps = ActionProps & ActionButtonProps;
|
|
15
|
+
|
|
15
16
|
type LinkActionProps = ActionProps & LinkProps;
|
|
16
17
|
|
|
17
|
-
export
|
|
18
|
+
export type HeaderProps = CommonProps & {
|
|
18
19
|
/**
|
|
19
|
-
*
|
|
20
|
-
* is provided, a `Link` will be rendered instead of a `Button`.
|
|
20
|
+
* When the `href` property is provided to the `action`, we will render a `Link` instead of a `ActionButton`.
|
|
21
21
|
*/
|
|
22
22
|
action?: ButtonActionProps | LinkActionProps;
|
|
23
|
-
|
|
24
23
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* - When `as="legend"`, the `Header` will render as a `<legend>` element.
|
|
28
|
-
* **Note:** `<legend>` elements must be the first child of a `<fieldset>` to comply with HTML semantics.
|
|
29
|
-
* If this condition is not met, a warning will be logged to the console.
|
|
30
|
-
* Additionally, the `action` prop is not supported when `as="legend"`.
|
|
24
|
+
* Override the heading element rendered for the title, useful to specify the semantics of your header.
|
|
31
25
|
*
|
|
32
|
-
*
|
|
26
|
+
* @default "h5"
|
|
33
27
|
*/
|
|
34
28
|
as?: Heading | 'legend' | 'header';
|
|
35
|
-
|
|
36
|
-
/** Required prop to set the title of the Header. */
|
|
37
29
|
title: string;
|
|
30
|
+
} & Pick<HTMLAttributes<HTMLDivElement>, 'role' | 'id'>;
|
|
38
31
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
testId?: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Renders a header action which can be either a button or a link.
|
|
48
|
-
*
|
|
49
|
-
* @param {Object} props - The properties object.
|
|
50
|
-
* @param {ButtonActionProps | LinkActionProps} props.action - The action object which can be either a button or a link.
|
|
51
|
-
* @returns {JSX.Element} The rendered header action component.
|
|
52
|
-
*/
|
|
53
|
-
const HeaderAction = React.forwardRef(
|
|
54
|
-
(
|
|
55
|
-
{ action }: { action: ButtonActionProps | LinkActionProps },
|
|
56
|
-
ref: React.Ref<HTMLButtonElement | HTMLAnchorElement>,
|
|
57
|
-
) => {
|
|
58
|
-
const { 'aria-label': ariaLabel, text, onClick } = action;
|
|
59
|
-
|
|
60
|
-
if ('href' in action) {
|
|
61
|
-
const { href, target, onClick: linkOnClick } = action;
|
|
62
|
-
return (
|
|
63
|
-
<Link
|
|
64
|
-
ref={ref as React.Ref<HTMLAnchorElement>}
|
|
65
|
-
href={href}
|
|
66
|
-
target={target}
|
|
67
|
-
aria-label={ariaLabel}
|
|
68
|
-
onClick={linkOnClick}
|
|
69
|
-
>
|
|
70
|
-
{text}
|
|
71
|
-
</Link>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
32
|
+
const HeaderAction = ({ action }: { action: ButtonActionProps | LinkActionProps }) => {
|
|
33
|
+
const props = {
|
|
34
|
+
'aria-label': action['aria-label'],
|
|
35
|
+
};
|
|
74
36
|
|
|
37
|
+
if ('href' in action) {
|
|
75
38
|
return (
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
priority="tertiary"
|
|
80
|
-
size="sm"
|
|
81
|
-
aria-label={ariaLabel}
|
|
82
|
-
onClick={onClick}
|
|
83
|
-
>
|
|
84
|
-
{text}
|
|
85
|
-
</Button>
|
|
39
|
+
<Link href={action.href} target={action.target} onClick={action.onClick} {...props}>
|
|
40
|
+
{action.text}
|
|
41
|
+
</Link>
|
|
86
42
|
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Button
|
|
47
|
+
className="np-header__button"
|
|
48
|
+
priority="tertiary"
|
|
49
|
+
size="sm"
|
|
50
|
+
onClick={action.onClick}
|
|
51
|
+
{...props}
|
|
52
|
+
>
|
|
53
|
+
{action.text}
|
|
54
|
+
</Button>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
91
57
|
|
|
92
58
|
/**
|
|
93
|
-
* @param {ButtonActionProps | LinkActionProps} [action] - Optional prop to specify the action button or link.
|
|
94
|
-
* @param {Heading | 'legend'} [as='h5'] - Optional prop to override the heading element rendered for the title.
|
|
95
|
-
* @param {string} title - Required prop to set the title of the section header.
|
|
96
|
-
* @param {'group' | 'section'} [level='group'] - Optional prop to specify the level of the section header.
|
|
97
|
-
* @param {string} [className]
|
|
98
|
-
* @param {string} [testId]
|
|
99
59
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
60
|
+
* Neptune Web: https://transferwise.github.io/neptune-web/components/content/Header
|
|
61
|
+
*
|
|
102
62
|
*/
|
|
103
|
-
const Header
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
'np-header--section': level === 'section',
|
|
113
|
-
'np-header__title': !action || as === 'legend',
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
const commonProps = {
|
|
117
|
-
className: headerClasses,
|
|
118
|
-
'data-testid': testId,
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
useEffect(() => {
|
|
122
|
-
if (as === 'legend' && internalRef.current) {
|
|
123
|
-
const { parentElement } = internalRef.current;
|
|
124
|
-
if (!parentElement || parentElement.tagName.toLowerCase() !== 'fieldset') {
|
|
125
|
-
console.warn(
|
|
126
|
-
'Legends should be the first child in a fieldset, and this is not possible when including an action',
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}, [as]);
|
|
131
|
-
|
|
132
|
-
if (!action || as === 'legend') {
|
|
133
|
-
return (
|
|
134
|
-
<Title ref={internalRef} as={as} type={levelTypography} {...commonProps} {...props}>
|
|
135
|
-
{title}
|
|
136
|
-
</Title>
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const actionRef = React.createRef<HTMLButtonElement | HTMLAnchorElement>();
|
|
141
|
-
|
|
63
|
+
export const Header = ({
|
|
64
|
+
id,
|
|
65
|
+
action,
|
|
66
|
+
as = 'h5',
|
|
67
|
+
title,
|
|
68
|
+
className,
|
|
69
|
+
role = undefined,
|
|
70
|
+
}: HeaderProps) => {
|
|
71
|
+
if (!action) {
|
|
142
72
|
return (
|
|
143
|
-
<
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
73
|
+
<Title
|
|
74
|
+
as={as}
|
|
75
|
+
id={id}
|
|
76
|
+
role={role}
|
|
77
|
+
type={Typography.TITLE_GROUP}
|
|
78
|
+
className={clsx('np-header', 'np-header__title', className)}
|
|
79
|
+
>
|
|
80
|
+
{title}
|
|
81
|
+
</Title>
|
|
149
82
|
);
|
|
150
|
-
}
|
|
151
|
-
);
|
|
83
|
+
}
|
|
152
84
|
|
|
153
|
-
|
|
85
|
+
if (as === 'legend') {
|
|
86
|
+
// eslint-disable-next-line no-console
|
|
87
|
+
console.warn(
|
|
88
|
+
'Legends should be the first child in a fieldset, and this is not possible when including an action',
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div className={clsx('np-header', className)}>
|
|
94
|
+
<Title as={as} type={Typography.TITLE_GROUP} id={id} className="np-header__title">
|
|
95
|
+
{title}
|
|
96
|
+
</Title>
|
|
97
|
+
<HeaderAction action={action} />
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
154
101
|
|
|
155
102
|
export default Header;
|
package/src/header/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -32,7 +32,6 @@ export type { DefinitionListProps, DefinitionListDefinition } from './definition
|
|
|
32
32
|
export type { DimmerProps } from './dimmer';
|
|
33
33
|
export type { DrawerProps } from './drawer';
|
|
34
34
|
export type { DividerProps } from './divider';
|
|
35
|
-
export type { HeaderProps } from './header';
|
|
36
35
|
export type { EmphasisProps } from './emphasis';
|
|
37
36
|
export type { FieldProps } from './field/Field';
|
|
38
37
|
export type { InfoProps } from './info';
|
package/src/main.css
CHANGED
|
@@ -2462,36 +2462,30 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
2462
2462
|
fill: var(--color-interactive-primary);
|
|
2463
2463
|
}
|
|
2464
2464
|
.np-header {
|
|
2465
|
+
display: flex;
|
|
2466
|
+
justify-content: space-between;
|
|
2465
2467
|
align-items: flex-end;
|
|
2468
|
+
max-width: 100%;
|
|
2469
|
+
padding: 8px 0;
|
|
2470
|
+
padding: var(--size-8) 0;
|
|
2466
2471
|
border-bottom: 1px solid rgba(0,0,0,0.10196);
|
|
2467
2472
|
border-bottom: 1px solid var(--color-border-neutral);
|
|
2473
|
+
margin-bottom: 8px;
|
|
2474
|
+
margin-bottom: var(--size-8);
|
|
2468
2475
|
-moz-column-gap: 24px;
|
|
2469
2476
|
column-gap: 24px;
|
|
2470
2477
|
-moz-column-gap: var(--size-24);
|
|
2471
2478
|
column-gap: var(--size-24);
|
|
2472
|
-
display: flex;
|
|
2473
|
-
justify-content: space-between;
|
|
2474
|
-
margin-bottom: 8px;
|
|
2475
|
-
margin-bottom: var(--size-8);
|
|
2476
|
-
max-width: 100%;
|
|
2477
|
-
padding: 8px 0;
|
|
2478
|
-
padding: var(--size-8) 0;
|
|
2479
|
-
width: 100%;
|
|
2480
|
-
}
|
|
2481
|
-
.np-header--section {
|
|
2482
|
-
align-items: center;
|
|
2483
|
-
border-bottom: none;
|
|
2484
|
-
}
|
|
2485
|
-
.np-header--section .np-header__button {
|
|
2486
|
-
margin-block-end: 0;
|
|
2487
2479
|
}
|
|
2488
2480
|
.np-header__title {
|
|
2489
2481
|
color: #5d7079;
|
|
2490
2482
|
color: var(--color-content-secondary);
|
|
2491
2483
|
}
|
|
2492
2484
|
.np-header__button {
|
|
2493
|
-
margin-inline: calc(
|
|
2494
|
-
margin-
|
|
2485
|
+
margin-inline: calc(16px * -1);
|
|
2486
|
+
margin-inline: calc(var(--size-16) * -1);
|
|
2487
|
+
margin-bottom: calc(4px * -1);
|
|
2488
|
+
margin-bottom: calc(var(--size-4) * -1);
|
|
2495
2489
|
}
|
|
2496
2490
|
.tw-image {
|
|
2497
2491
|
max-width: none;
|
package/src/title/Title.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
|
-
import {
|
|
2
|
+
import { LabelHTMLAttributes, LiHTMLAttributes, ReactHTML } from 'react';
|
|
3
3
|
|
|
4
4
|
import { TitleTypes, Typography, Heading } from '../common';
|
|
5
5
|
|
|
@@ -25,29 +25,15 @@ type Props = LabelHTMLAttributes<HTMLHeadingElement | HTMLSpanElement | HTMLLabe
|
|
|
25
25
|
type?: TitleTypes;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
className={clsx(`np-text-${type}`, className)}
|
|
39
|
-
/>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
const HeaderTag = as ?? titleTypeMapping[DEFAULT_TYPE];
|
|
43
|
-
return (
|
|
44
|
-
<HeaderTag
|
|
45
|
-
ref={ref as React.Ref<any>}
|
|
46
|
-
{...props}
|
|
47
|
-
className={clsx(`np-text-${DEFAULT_TYPE}`, className)}
|
|
48
|
-
/>
|
|
49
|
-
);
|
|
50
|
-
},
|
|
51
|
-
);
|
|
28
|
+
function Title({ as, type = DEFAULT_TYPE, className, ...props }: Props) {
|
|
29
|
+
const mapping = titleTypeMapping[type];
|
|
30
|
+
const isTypeSupported = mapping !== undefined;
|
|
31
|
+
if (isTypeSupported) {
|
|
32
|
+
const HeaderTag = as ?? mapping;
|
|
33
|
+
return <HeaderTag {...props} className={clsx(`np-text-${type}`, className)} />;
|
|
34
|
+
}
|
|
35
|
+
const HeaderTag = as ?? titleTypeMapping[DEFAULT_TYPE];
|
|
36
|
+
return <HeaderTag {...props} className={clsx(`np-text-${DEFAULT_TYPE}`, className)} />;
|
|
37
|
+
}
|
|
52
38
|
|
|
53
39
|
export default Title;
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { Meta, Canvas, Source } from '@storybook/blocks';
|
|
2
|
-
import { NavigationOption } from '..';
|
|
3
|
-
import { Bulb } from '@transferwise/icons';
|
|
4
|
-
import * as stories from './Header.story';
|
|
5
|
-
|
|
6
|
-
<Meta title="Typography/Header/Accessibility" />
|
|
7
|
-
|
|
8
|
-
# Accessibility
|
|
9
|
-
|
|
10
|
-
Given the `Header` is a key component for structuring content and conveying hierarchy, ensuring its accessibility is crucial for an inclusive experience.
|
|
11
|
-
|
|
12
|
-
<NavigationOption
|
|
13
|
-
media={<Bulb size={24} />}
|
|
14
|
-
title="Design guidance"
|
|
15
|
-
content="Before you start, familiarise yourself with the dedicated accessibility documentation."
|
|
16
|
-
href="https://wise.design/components/section-header#accessibility"
|
|
17
|
-
/>
|
|
18
|
-
|
|
19
|
-
<br />
|
|
20
|
-
<br />
|
|
21
|
-
|
|
22
|
-
## Semantic headings
|
|
23
|
-
|
|
24
|
-
The `Header` component should always use semantic HTML heading tags (`<h1>` to `<h6>`) to convey the document structure. Avoid using non-semantic tags unless absolutely necessary.
|
|
25
|
-
|
|
26
|
-
<Source dark code={`
|
|
27
|
-
// ✅ semantic heading
|
|
28
|
-
<Header as="h1">Main heading</Header>
|
|
29
|
-
|
|
30
|
-
// ⚠️ use with care
|
|
31
|
-
|
|
32
|
-
<Header as="div">Non-semantic heading</Header>
|
|
33
|
-
`}/>
|
|
34
|
-
|
|
35
|
-
**Additional resources:**
|
|
36
|
-
|
|
37
|
-
1. [Deque: Headings must follow a logical order](https://dequeuniversity.com/rules/axe/4.2/heading-order)
|
|
38
|
-
2. [MDN: Using headings](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements)
|
|
39
|
-
|
|
40
|
-
<br />
|
|
41
|
-
|
|
42
|
-
## ARIA roles
|
|
43
|
-
|
|
44
|
-
If the `Header` is used in a non-semantic context, ensure it is accessible by applying appropriate ARIA roles, such as `role="heading"`, and providing a `aria-level` attribute to indicate its level in the hierarchy.
|
|
45
|
-
|
|
46
|
-
<Source
|
|
47
|
-
dark
|
|
48
|
-
code={`
|
|
49
|
-
// ⚠️ use with care
|
|
50
|
-
<Header as="div" role="heading" aria-level="1">
|
|
51
|
-
Main heading
|
|
52
|
-
</Header>
|
|
53
|
-
`}
|
|
54
|
-
/>
|
|
55
|
-
|
|
56
|
-
<br />
|
|
57
|
-
|
|
58
|
-
## Accessibility with actions
|
|
59
|
-
|
|
60
|
-
When using the `Header` with actions (e.g., buttons or links), ensure the actions are accessible by providing clear labels and ARIA attributes.
|
|
61
|
-
|
|
62
|
-
<Source
|
|
63
|
-
dark
|
|
64
|
-
code={`
|
|
65
|
-
// ✅ accessible action
|
|
66
|
-
<Header
|
|
67
|
-
as="h2"
|
|
68
|
-
action={{
|
|
69
|
-
'aria-label': 'Edit section',
|
|
70
|
-
text: 'Edit',
|
|
71
|
-
onClick: () => alert('Edit clicked'),
|
|
72
|
-
}}
|
|
73
|
-
>
|
|
74
|
-
Section heading
|
|
75
|
-
</Header>
|
|
76
|
-
`}
|
|
77
|
-
/>
|
|
78
|
-
|
|
79
|
-
<br />
|
|
80
|
-
|
|
81
|
-
## Visual hierarchy
|
|
82
|
-
|
|
83
|
-
Ensure the `Header` visually aligns with its semantic level. For example, an `<h1>` should be the largest and most prominent, while an `<h6>` should be the smallest.
|
|
84
|
-
|
|
85
|
-
<Canvas of={stories.Playground} />
|