Compound Typography pass (#11103)
* Integrate compound design tokens The icons should not be included in this repo, and should live in the compound design token repo, but for simplicity sake at this phase of the integration they will be added here * Delete unused or incorrect - sass variables * Typography pass * Deprecate _font-weights.pcss and use Compound instead * lint fix * Fix snapshot * Fix typography pass feedback * Remove unwanted e2e test cypress tests should test functionality not visual output. And we should not test visual output by inspecting CSS properties * lintfix * Migration script for baseFontSize * Updates after design review * Update font scaling panel to use min/max size * Fix custom font * Fix font slider e2e test * Update custom font * Update new baseFontSizeV2 * Disambiguate heading props * Fix appearance test * change max font size * fix e2ee test * fix tests * test baseFontSize migration code * typescript strict * Migrate baseFontSize account setting * Change assertion for font size * Fix font size controller test
This commit is contained in:
parent
ce479c5774
commit
9c7d935aae
199 changed files with 606 additions and 608 deletions
|
@ -94,7 +94,7 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
|
|||
<BaseCard
|
||||
header={
|
||||
<div className="mx_BaseCard_header_title">
|
||||
<Heading size="h4" className="mx_BaseCard_header_title_heading">
|
||||
<Heading size="4" className="mx_BaseCard_header_title_heading">
|
||||
{_t("Notifications")}
|
||||
</Heading>
|
||||
</div>
|
||||
|
|
|
@ -113,7 +113,7 @@ export const ThreadPanelHeader: React.FC<{
|
|||
) : null;
|
||||
return (
|
||||
<div className="mx_BaseCard_header_title">
|
||||
<Heading size="h4" className="mx_BaseCard_header_title_heading">
|
||||
<Heading size="4" className="mx_BaseCard_header_title_heading">
|
||||
{_t("Threads")}
|
||||
</Heading>
|
||||
{!empty && (
|
||||
|
|
|
@ -357,7 +357,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
|||
private renderThreadViewHeader = (): JSX.Element => {
|
||||
return (
|
||||
<div className="mx_BaseCard_header_title">
|
||||
<Heading size="h4" className="mx_BaseCard_header_title_heading">
|
||||
<Heading size="4" className="mx_BaseCard_header_title_heading">
|
||||
{_t("Thread")}
|
||||
</Heading>
|
||||
<ThreadListContextMenu mxEvent={this.props.mxEvent} permalinkCreator={this.props.permalinkCreator} />
|
||||
|
|
|
@ -33,7 +33,7 @@ const DialogSidebar: React.FC<Props> = ({ beacons, onBeaconClick, requestClose }
|
|||
return (
|
||||
<div className="mx_DialogSidebar">
|
||||
<div className="mx_DialogSidebar_header">
|
||||
<Heading size="h4">{_t("View List")}</Heading>
|
||||
<Heading size="4">{_t("View List")}</Heading>
|
||||
<AccessibleButton
|
||||
className="mx_DialogSidebar_closeButton"
|
||||
onClick={requestClose}
|
||||
|
|
|
@ -54,7 +54,7 @@ export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
|||
>
|
||||
{desktopBuilds?.get("available") && (
|
||||
<div className="mx_AppDownloadDialog_desktop">
|
||||
<Heading size="h3">{_t("Download %(brand)s Desktop", { brand })}</Heading>
|
||||
<Heading size="3">{_t("Download %(brand)s Desktop", { brand })}</Heading>
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
element="a"
|
||||
|
@ -68,7 +68,7 @@ export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
|||
)}
|
||||
<div className="mx_AppDownloadDialog_mobile">
|
||||
<div className="mx_AppDownloadDialog_app">
|
||||
<Heading size="h3">{_t("iOS")}</Heading>
|
||||
<Heading size="3">{_t("iOS")}</Heading>
|
||||
<QRCode data={urlAppStore} margin={0} width={172} />
|
||||
<div className="mx_AppDownloadDialog_info">
|
||||
{_t("%(qrCode)s or %(appLinks)s", {
|
||||
|
@ -89,7 +89,7 @@ export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
|||
</div>
|
||||
</div>
|
||||
<div className="mx_AppDownloadDialog_app">
|
||||
<Heading size="h3">{_t("Android")}</Heading>
|
||||
<Heading size="3">{_t("Android")}</Heading>
|
||||
<QRCode data={urlAndroid} margin={0} width={172} />
|
||||
<div className="mx_AppDownloadDialog_info">
|
||||
{_t("%(qrCode)s or %(appLinks)s", {
|
||||
|
|
|
@ -174,7 +174,8 @@ export default class BaseDialog extends React.Component<IProps> {
|
|||
>
|
||||
{!!(this.props.title || headerImage) && (
|
||||
<Heading
|
||||
size="h2"
|
||||
as="h2"
|
||||
size="4"
|
||||
className={classNames("mx_Dialog_title", this.props.titleClass)}
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
|
|
|
@ -146,7 +146,7 @@ export default class AppPermission extends React.Component<IProps, IState> {
|
|||
<div className="mx_AppPermission_content_bolder">{_t("Widget added by")}</div>
|
||||
<div>
|
||||
{avatar}
|
||||
<Heading size="h4">{displayName}</Heading>
|
||||
<Heading size="4">{displayName}</Heading>
|
||||
<div>{userId}</div>
|
||||
</div>
|
||||
<div>{warning}</div>
|
||||
|
|
|
@ -31,7 +31,7 @@ export const EnableLiveShare: React.FC<Props> = ({ onSubmit }) => {
|
|||
return (
|
||||
<div data-testid="location-picker-enable-live-share" className="mx_EnableLiveShare">
|
||||
<StyledLiveBeaconIcon className="mx_EnableLiveShare_icon" />
|
||||
<Heading className="mx_EnableLiveShare_heading" size="h3">
|
||||
<Heading className="mx_EnableLiveShare_heading" size="3">
|
||||
{_t("Live location sharing")}
|
||||
</Heading>
|
||||
<p className="mx_EnableLiveShare_description">
|
||||
|
|
|
@ -38,7 +38,7 @@ export const MapError: React.FC<MapErrorProps> = ({ error, isMinimised, classNam
|
|||
onClick={onClick}
|
||||
>
|
||||
<WarningBadge className="mx_MapError_icon" />
|
||||
<Heading className="mx_MapError_heading" size="h3">
|
||||
<Heading className="mx_MapError_heading" size="3">
|
||||
{_t("Unable to load map")}
|
||||
</Heading>
|
||||
<p className="mx_MapError_message">{getLocationShareErrorMessage(error)}</p>
|
||||
|
|
|
@ -81,7 +81,7 @@ const ShareType: React.FC<Props> = ({ setShareType, enabledShareTypes }) => {
|
|||
return (
|
||||
<div className="mx_ShareType">
|
||||
<LocationIcon className="mx_ShareType_badge" />
|
||||
<Heading className="mx_ShareType_heading" size="h3">
|
||||
<Heading className="mx_ShareType_heading" size="3">
|
||||
{_t("What location type do you want to share?")}
|
||||
</Heading>
|
||||
<div className="mx_ShareType_wrapper_options">
|
||||
|
|
|
@ -69,7 +69,7 @@ export const PollHistory: React.FC<PollHistoryProps> = ({ room, matrixClient, pe
|
|||
return (
|
||||
<div className="mx_PollHistory_content">
|
||||
{/* @TODO this probably needs some style */}
|
||||
<Heading className="mx_PollHistory_header" size="h2">
|
||||
<Heading className="mx_PollHistory_header" size="2">
|
||||
{title}
|
||||
</Heading>
|
||||
{focusedPoll ? (
|
||||
|
|
|
@ -179,7 +179,7 @@ const PinnedMessagesCard: React.FC<IProps> = ({ room, onClose, permalinkCreator
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<Heading size="h4" className="mx_PinnedMessagesCard_empty_header">
|
||||
<Heading size="4" className="mx_PinnedMessagesCard_empty_header">
|
||||
{_t("Nothing pinned, yet")}
|
||||
</Heading>
|
||||
{_t(
|
||||
|
@ -225,7 +225,7 @@ const PinnedMessagesCard: React.FC<IProps> = ({ room, onClose, permalinkCreator
|
|||
<BaseCard
|
||||
header={
|
||||
<div className="mx_BaseCard_header_title">
|
||||
<Heading size="h4" className="mx_BaseCard_header_title_heading">
|
||||
<Heading size="4" className="mx_BaseCard_header_title_heading">
|
||||
{_t("Pinned messages")}
|
||||
</Heading>
|
||||
</div>
|
||||
|
|
|
@ -190,7 +190,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
|
|||
private renderTimelineCardHeader = (): JSX.Element => {
|
||||
return (
|
||||
<div className="mx_BaseCard_header_title">
|
||||
<Heading size="h4" className="mx_BaseCard_header_title_heading">
|
||||
<Heading size="4" className="mx_BaseCard_header_title_heading">
|
||||
{_t("Chat")}
|
||||
</Heading>
|
||||
</div>
|
||||
|
|
|
@ -73,7 +73,7 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
|
|||
|
||||
const header = (
|
||||
<div className="mx_BaseCard_header_title">
|
||||
<Heading size="h4" className="mx_BaseCard_header_title_heading">
|
||||
<Heading size="4" className="mx_BaseCard_header_title_heading">
|
||||
{WidgetUtils.getWidgetName(app)}
|
||||
</Heading>
|
||||
<ContextMenuButton
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2021 - 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -54,7 +54,7 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
fontSize: (SettingsStore.getValue("baseFontSize", null) + FontWatcher.SIZE_DIFF).toString(),
|
||||
fontSize: SettingsStore.getValue("baseFontSizeV2", null).toString(),
|
||||
useCustomFontSize: SettingsStore.getValue("useCustomFontSize"),
|
||||
layout: SettingsStore.getValue("layout"),
|
||||
};
|
||||
|
@ -80,13 +80,13 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
|||
|
||||
private onFontSizeChanged = (size: number): void => {
|
||||
this.setState({ fontSize: size.toString() });
|
||||
SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, size - FontWatcher.SIZE_DIFF);
|
||||
SettingsStore.setValue("baseFontSizeV2", null, SettingLevel.DEVICE, size);
|
||||
};
|
||||
|
||||
private onValidateFontSize = async ({ value }: Pick<IFieldState, "value">): Promise<IValidationResult> => {
|
||||
const parsedSize = parseFloat(value!);
|
||||
const min = FontWatcher.MIN_SIZE + FontWatcher.SIZE_DIFF;
|
||||
const max = FontWatcher.MAX_SIZE + FontWatcher.SIZE_DIFF;
|
||||
const min = FontWatcher.MIN_SIZE;
|
||||
const max = FontWatcher.MAX_SIZE;
|
||||
|
||||
if (isNaN(parsedSize)) {
|
||||
return { valid: false, feedback: _t("Size must be a number") };
|
||||
|
@ -99,15 +99,12 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
|||
};
|
||||
}
|
||||
|
||||
SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, parseInt(value!, 10) - FontWatcher.SIZE_DIFF);
|
||||
SettingsStore.setValue("baseFontSizeV2", null, SettingLevel.DEVICE, parseInt(value!, 10));
|
||||
|
||||
return { valid: true, feedback: _t("Use between %(min)s pt and %(max)s pt", { min, max }) };
|
||||
};
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const min = 13;
|
||||
const max = 18;
|
||||
|
||||
return (
|
||||
<SettingsSubsection heading={_t("Font size")} stretchContent data-testid="mx_FontScalingPanel">
|
||||
<EventTilePreview
|
||||
|
@ -121,8 +118,8 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
|||
<div className="mx_FontScalingPanel_fontSlider">
|
||||
<div className="mx_FontScalingPanel_fontSlider_smallText">Aa</div>
|
||||
<Slider
|
||||
min={min}
|
||||
max={max}
|
||||
min={FontWatcher.MIN_SIZE}
|
||||
max={FontWatcher.MAX_SIZE}
|
||||
step={1}
|
||||
value={parseInt(this.state.fontSize, 10)}
|
||||
onChange={this.onFontSizeChanged}
|
||||
|
@ -140,7 +137,7 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
|||
this.setState({ useCustomFontSize: checked });
|
||||
if (!checked) {
|
||||
const size = parseInt(this.state.fontSize, 10);
|
||||
const clamped = clamp(size, min, max);
|
||||
const clamped = clamp(size, FontWatcher.MIN_SIZE, FontWatcher.MAX_SIZE);
|
||||
if (clamped !== size) {
|
||||
this.onFontSizeChanged(clamped);
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ export default class IntegrationManager extends React.Component<IProps, IState>
|
|||
if (this.props.loading) {
|
||||
return (
|
||||
<div className="mx_IntegrationManager_loading">
|
||||
<Heading size="h3">{_t("Connecting to integration manager…")}</Heading>
|
||||
<Heading size="3">{_t("Connecting to integration manager…")}</Heading>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
|
@ -98,7 +98,7 @@ export default class IntegrationManager extends React.Component<IProps, IState>
|
|||
if (!this.props.connected || this.state.errored) {
|
||||
return (
|
||||
<div className="mx_IntegrationManager_error">
|
||||
<Heading size="h3">{_t("Cannot connect to integration manager")}</Heading>
|
||||
<Heading size="3">{_t("Cannot connect to integration manager")}</Heading>
|
||||
<p>{_t("The integration manager is offline or it cannot reach your homeserver.")}</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -79,8 +79,8 @@ export default class SetIntegrationManager extends React.Component<IProps, IStat
|
|||
>
|
||||
<div className="mx_SettingsFlag">
|
||||
<div className="mx_SetIntegrationManager_heading_manager">
|
||||
<Heading size="h2">{_t("Manage integrations")}</Heading>
|
||||
<Heading size="h3">{managerName}</Heading>
|
||||
<Heading size="2">{_t("Manage integrations")}</Heading>
|
||||
<Heading size="3">{managerName}</Heading>
|
||||
</div>
|
||||
<ToggleSwitch
|
||||
id="toggle_integration"
|
||||
|
|
|
@ -134,7 +134,7 @@ export const DeviceDetailHeading: React.FC<Props> = ({ device, saveDeviceName })
|
|||
<DeviceNameEditor device={device} saveDeviceName={saveDeviceName} stopEditing={() => setIsEditing(false)} />
|
||||
) : (
|
||||
<div className="mx_DeviceDetailHeading" data-testid="device-detail-heading">
|
||||
<Heading size="h4">{device.display_name || device.device_id}</Heading>
|
||||
<Heading size="4">{device.display_name || device.device_id}</Heading>
|
||||
<AccessibleButton
|
||||
kind="link_inline"
|
||||
onClick={() => setIsEditing(true)}
|
||||
|
|
|
@ -30,7 +30,7 @@ export interface DeviceTileProps {
|
|||
}
|
||||
|
||||
const DeviceTileName: React.FC<{ device: ExtendedDevice }> = ({ device }) => {
|
||||
return <Heading size="h4">{device.display_name || device.device_id}</Heading>;
|
||||
return <Heading size="4">{device.display_name || device.device_id}</Heading>;
|
||||
};
|
||||
|
||||
const DeviceTile: React.FC<DeviceTileProps> = ({ device, children, isSelected, onClick }) => {
|
||||
|
|
|
@ -42,7 +42,7 @@ export interface SettingsSectionProps extends HTMLAttributes<HTMLDivElement> {
|
|||
*/
|
||||
export const SettingsSection: React.FC<SettingsSectionProps> = ({ heading, children, ...rest }) => (
|
||||
<div {...rest} className="mx_SettingsSection">
|
||||
{typeof heading === "string" ? <Heading size="h2">{heading}</Heading> : <>{heading}</>}
|
||||
{typeof heading === "string" ? <Heading size="2">{heading}</Heading> : <>{heading}</>}
|
||||
<div className="mx_SettingsSection_subSections">{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -25,7 +25,7 @@ export interface SettingsSubsectionHeadingProps extends HTMLAttributes<HTMLDivEl
|
|||
|
||||
export const SettingsSubsectionHeading: React.FC<SettingsSubsectionHeadingProps> = ({ heading, children, ...rest }) => (
|
||||
<div {...rest} className="mx_SettingsSubsectionHeading">
|
||||
<Heading className="mx_SettingsSubsectionHeading_heading" size="h3">
|
||||
<Heading className="mx_SettingsSubsectionHeading_heading" size="4" as="h3">
|
||||
{heading}
|
||||
</Heading>
|
||||
{children}
|
||||
|
|
|
@ -274,7 +274,7 @@ export default class NotificationsSettingsTab extends React.Component<IProps, IS
|
|||
</AccessibleButton>
|
||||
</div>
|
||||
<div>
|
||||
<h4>{_t("Set a new custom sound")}</h4>
|
||||
<h4 className="mx_Heading_h4">{_t("Set a new custom sound")}</h4>
|
||||
<div className="mx_SettingsFlag">
|
||||
<form autoComplete="off" noValidate={true}>
|
||||
<input
|
||||
|
|
|
@ -541,7 +541,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
|||
/>
|
||||
) : null;
|
||||
const heading = (
|
||||
<Heading size="h2">
|
||||
<Heading size="2">
|
||||
{discoWarning}
|
||||
{_t("Discovery")}
|
||||
</Heading>
|
||||
|
|
|
@ -17,15 +17,26 @@ limitations under the License.
|
|||
import React, { HTMLAttributes } from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Size = "h1" | "h2" | "h3" | "h4";
|
||||
type Size = "1" | "2" | "3" | "4";
|
||||
|
||||
type HTMLHeadingTags = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
||||
|
||||
interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
|
||||
/**
|
||||
* Defines the type of heading used
|
||||
*/
|
||||
as?: HTMLHeadingTags;
|
||||
/**
|
||||
* Defines the appearance of the heading
|
||||
* Falls back to the type of heading used if `as` is not provided
|
||||
*/
|
||||
size: Size;
|
||||
}
|
||||
|
||||
const Heading: React.FC<HeadingProps> = ({ size, className, children, ...rest }) =>
|
||||
React.createElement(size || "h1", {
|
||||
const Heading: React.FC<HeadingProps> = ({ as, size = "1", className, children, ...rest }) =>
|
||||
React.createElement(as || `h${size}`, {
|
||||
...rest,
|
||||
className: classNames(`mx_Heading_${size}`, className),
|
||||
className: classNames(`mx_Heading_h${size}`, className),
|
||||
children,
|
||||
});
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ function UserOnboardingButtonInternal({ selected, minimized }: Props): JSX.Eleme
|
|||
{!minimized && (
|
||||
<>
|
||||
<div className="mx_UserOnboardingButton_content">
|
||||
<Heading size="h4" className="mx_Heading_h4">
|
||||
<Heading size="4" className="mx_Heading_h4">
|
||||
{_t("Welcome")}
|
||||
</Heading>
|
||||
<AccessibleButton className="mx_UserOnboardingButton_close" onClick={onDismiss} />
|
||||
|
|
|
@ -92,7 +92,7 @@ export function UserOnboardingHeader({ useCase }: Props): JSX.Element {
|
|||
return (
|
||||
<div className="mx_UserOnboardingHeader">
|
||||
<div className="mx_UserOnboardingHeader_content">
|
||||
<Heading size="h1">
|
||||
<Heading size="1">
|
||||
{title}
|
||||
<span className="mx_UserOnboardingHeader_dot">.</span>
|
||||
</Heading>
|
||||
|
|
|
@ -50,7 +50,7 @@ export function UserOnboardingList({ tasks }: Props): JSX.Element {
|
|||
return (
|
||||
<div className="mx_UserOnboardingList" data-testid="user-onboarding-list">
|
||||
<div className="mx_UserOnboardingList_header">
|
||||
<Heading size="h3" className="mx_UserOnboardingList_title">
|
||||
<Heading size="3" className="mx_UserOnboardingList_title">
|
||||
{waiting > 0
|
||||
? _t("Only %(count)s steps to go", {
|
||||
count: waiting,
|
||||
|
|
|
@ -45,7 +45,7 @@ export function UserOnboardingTask({ task, completed = false }: Props): JSX.Elem
|
|||
aria-labelledby={`mx_UserOnboardingTask_${task.id}`}
|
||||
/>
|
||||
<div id={`mx_UserOnboardingTask_${task.id}`} className="mx_UserOnboardingTask_content">
|
||||
<Heading size="h4" className="mx_UserOnboardingTask_title">
|
||||
<Heading size="4" className="mx_UserOnboardingTask_title">
|
||||
{title}
|
||||
</Heading>
|
||||
<div className="mx_UserOnboardingTask_description">{description}</div>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue