diff --git a/src/components/Attachment/AttachmentContainer.tsx b/src/components/Attachment/AttachmentContainer.tsx index c119cff28..95dcb2332 100644 --- a/src/components/Attachment/AttachmentContainer.tsx +++ b/src/components/Attachment/AttachmentContainer.tsx @@ -330,7 +330,7 @@ export const UnsupportedAttachmentContainer = ({ attachment, UnsupportedAttachment = DefaultUnsupportedAttachment, }: RenderAttachmentProps) => ( - <> + - + ); diff --git a/src/components/Attachment/UnsupportedAttachment.tsx b/src/components/Attachment/UnsupportedAttachment.tsx index df7c7976d..928d89654 100644 --- a/src/components/Attachment/UnsupportedAttachment.tsx +++ b/src/components/Attachment/UnsupportedAttachment.tsx @@ -2,7 +2,7 @@ import React from 'react'; import type { Attachment } from 'stream-chat'; import { FileIcon } from '../FileIcon'; -import { useTranslationContext } from '../../context'; +import { useComponentContext, useTranslationContext } from '../../context'; export type UnsupportedAttachmentProps = { attachment: Attachment; @@ -10,12 +10,18 @@ export type UnsupportedAttachmentProps = { export const UnsupportedAttachment = ({ attachment }: UnsupportedAttachmentProps) => { const { t } = useTranslationContext('UnsupportedAttachment'); + const { AttachmentFileIcon } = useComponentContext(); + const FileIconComponent = AttachmentFileIcon ?? FileIcon; return (
- +
= {}, +) => + render( + + + + + , + ); + +describe('UnsupportedAttachment', () => { + it('renders attachment title when title is set', () => { + renderUnsupported({ + mime_type: 'application/x-custom', + title: 'Custom payload', + type: 'unknown', + } as Attachment); + + expect(screen.getByTestId('unsupported-attachment-title')).toHaveTextContent( + 'Custom payload', + ); + }); + + it('falls back to translated label when title is missing', () => { + renderUnsupported({ type: 'weird' } as Attachment); + + expect(screen.getByTestId('unsupported-attachment-title')).toHaveTextContent( + 'Unsupported attachment', + ); + }); + + it('uses AttachmentFileIcon from context when provided', () => { + const CustomAttachmentFileIcon = ({ fileName, mimeType }: FileIconProps) => ( + + ); + + renderUnsupported( + { + mime_type: 'application/octet-stream', + title: 'data.bin', + type: 'file', + } as Attachment, + { AttachmentFileIcon: CustomAttachmentFileIcon }, + ); + + const icon = screen.getByTestId('custom-attachment-file-icon'); + expect(icon).toHaveAttribute('data-file-name', 'data.bin'); + expect(icon).toHaveAttribute('data-mime-type', 'application/octet-stream'); + }); +}); diff --git a/src/components/Attachment/styling/Attachment.scss b/src/components/Attachment/styling/Attachment.scss index 4239c2761..3edbb7c9b 100644 --- a/src/components/Attachment/styling/Attachment.scss +++ b/src/components/Attachment/styling/Attachment.scss @@ -338,34 +338,6 @@ } } - .str-chat__message-attachment-unsupported { - @include utils.component-layer-overrides('file-attachment'); - display: flex; - align-items: center; - justify-content: center; - padding: var(--space-8); - column-gap: var(--space-16); - //margin: var(--str-chat__attachment-margin); - - .str-chat__file-icon { - width: calc(var(--str-chat__spacing-px) * 30); - } - - .str-chat__message-attachment-unsupported__metadata { - min-width: 0; - flex: 1; - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: center; - } - - .str-chat__message-attachment-unsupported__title { - @include utils.ellipsis-text; - max-width: 100%; - } - } - .str-chat__message-attachment-file--item, .str-chat__message-attachment-audio-widget { @include utils.flex-row-center; diff --git a/src/components/Attachment/styling/UnsupportedAttachment.scss b/src/components/Attachment/styling/UnsupportedAttachment.scss new file mode 100644 index 000000000..21b9bd638 --- /dev/null +++ b/src/components/Attachment/styling/UnsupportedAttachment.scss @@ -0,0 +1,66 @@ +@use '../../../styling/utils'; +@use '../../../styling/variables/font' as font; + +.str-chat { + /* The border radius used for the borders of file attachments */ + --str-chat__unsupported-attachment-border-radius: calc( + var(--str-chat__message-bubble-border-radius) - var(--str-chat__attachment-margin) + ); + + /* The text/icon color of file attachments */ + --str-chat__unsupported-attachment-color: var(--str-chat__text-color); + + /* The text/icon color of file attachments for low emphasis texts (for example file size) */ + --str-chat__unsupported-attachment-secondary-color: var( + --str-chat__text-low-emphasis-color + ); + + /* The background color of file attachments */ + --str-chat__unsupported-attachment-background-color: transparent; + + /* Top border of file attachments */ + --str-chat__unsupported-attachment-border-block-start: none; + + /* Bottom border of file attachments */ + --str-chat__unsupported-attachment-border-block-end: none; + + /* Left (right in RTL layout) border of file attachments */ + --str-chat__unsupported-attachment-border-inline-start: none; + + /* Right (left in RTL layout) border of file attachments */ + --str-chat__unsupported-attachment-border-inline-end: none; + + /* Box shadow applied to file attachments */ + --str-chat__unsupported-attachment-box-shadow: none; +} + +.str-chat__message-attachment-unsupported { + @include utils.component-layer-overrides('unsupported-attachment'); + display: flex; + align-items: center; + justify-content: center; + padding: var(--spacing-sm); + column-gap: var(--spacing-sm); + // todo: keep a single variable for attachment min width + min-width: 300px; + + .str-chat__file-icon { + width: calc(var(--str-chat__spacing-px) * 30); + } + + .str-chat__message-attachment-unsupported__metadata { + min-width: 0; + flex: 1; + height: stretch; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + } + + .str-chat__message-attachment-unsupported__title { + @include utils.ellipsis-text; + @include font.text-caption-emphasis; + max-width: 100%; + } +} diff --git a/src/components/Attachment/styling/index.scss b/src/components/Attachment/styling/index.scss index 13187343a..f30dbc237 100644 --- a/src/components/Attachment/styling/index.scss +++ b/src/components/Attachment/styling/index.scss @@ -6,3 +6,4 @@ @use 'Giphy'; @use 'LinkPreview'; @use 'ModalGallery'; +@use 'UnsupportedAttachment'; diff --git a/src/styling/index.scss b/src/styling/index.scss index 6ee705ed7..e0bb678f7 100644 --- a/src/styling/index.scss +++ b/src/styling/index.scss @@ -4,7 +4,8 @@ @use 'global-layout-variables'; @use 'global-theme-variables'; @use 'palette-variables'; -@use './variables.css'; +@use './variables-tokens.css'; +@use 'variables/font'; @use 'base'; @use 'fonts'; diff --git a/src/styling/variables.css b/src/styling/variables-tokens.css similarity index 100% rename from src/styling/variables.css rename to src/styling/variables-tokens.css diff --git a/src/styling/variables/font.scss b/src/styling/variables/font.scss new file mode 100644 index 000000000..7af868e36 --- /dev/null +++ b/src/styling/variables/font.scss @@ -0,0 +1,249 @@ +// HEADING +// ================================================================================= // +@mixin text-heading-xs { + font: var(--str-chat__font-body-default); + color: var(--text-primary); +} + +@mixin text-heading-sm { + font: var(--str-chat__font-body-emphasis); + color: var(--text-primary); +} + +@mixin text-heading-md { + font: var(--str-chat__font-body-link); + color: var(--text-link); +} + +@mixin text-heading-lg { + font: var(--str-chat__font-body-link-emphasis); + color: var(--text-link); +} + +// BODY +// ================================================================================= // +@mixin text-body-default { + font: var(--str-chat__font-body-default); + color: var(--text-primary); +} + +@mixin text-body-emphasis { + font: var(--str-chat__font-body-emphasis); + color: var(--text-primary); +} + +@mixin text-body-link { + font: var(--str-chat__font-body-link); + color: var(--text-link); +} + +@mixin text-body-link-emphasis { + font: var(--str-chat__font-body-link-emphasis); + color: var(--text-link); +} + +// CAPTION +// ================================================================================= // +@mixin text-caption-default { + font: var(--str-chat__font-caption-default); + color: var(--text-primary); +} + +@mixin text-caption-emphasis { + font: var(--str-chat__font-caption-emphasis); + color: var(--text-primary); +} + +@mixin text-caption-link { + font: var(--str-chat__font-caption-link); + color: var(--text-link); +} + +@mixin text-caption-link-emphasis { + font: var(--str-chat__font-caption-link-emphasis); + color: var(--text-link); +} + +// METADATA +// ================================================================================= // +@mixin text-metadata-default { + font: var(--str-chat__font-metadata-default); + color: var(--text-primary); +} + +@mixin text-metadata-emphasis { + font: var(--str-chat__font-metadata-emphasis); + color: var(--text-primary); +} + +@mixin text-metadata-link { + font: var(--str-chat__font-metadata-link); + color: var(--text-link); +} + +@mixin text-metadata-link-emphasis { + font: var(--str-chat__font-metadata-link-emphasis); + color: var(--text-link); +} + +// NUMERIC +/* Numeric styles are used for compact, layout-critical UI elements such as: + - badge counts, + - avatar initials, + - keyboard key labels, and + - numeric labels inside pills. + Numeric tokens are classified as layout-stable UI indicators, not content text. */ +// ================================================================================= // +@mixin text-numeric-sm { + font: var(--str-chat__font-numeric-sm); + color: var(--text-primary); +} + +@mixin text-numeric-md { + font: var(--str-chat__font-numeric-md); + color: var(--text-primary); +} + +@mixin text-numeric-lg { + font: var(--str-chat__font-numeric-lg); + color: var(--text-primary); +} + +@mixin text-numeric-xl { + font: var(--str-chat__font-numeric-xl); + color: var(--text-primary); +} + +.str-chat { + /* The font used in the chat, by default, we use [preinstalled OS fonts](https://systemfontstack.com/) */ + --str-chat__font-family: + var(--typography-font-family-sans), system-ui, -apple-system, BlinkMacSystemFont, + Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif; + + // HEADING + // ================================================================================= // + --str-chat__font-heading-xs: normal var(--typography-font-weight-semi-bold) + var(--typography-font-size-sm) / var(--typography-line-height-normal) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-heading-sm: normal var(--typography-font-weight-semi-bold) + var(--typography-font-size-md) / var(--typography-line-height-normal) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-heading-md: normal var(--typography-font-weight-semi-bold) + var(--typography-font-size-lg) / var(--typography-line-height-relaxed) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-heading-lg: normal var(--typography-font-weight-semi-bold) + var(--typography-font-size-xl) / var(--typography-line-height-relaxed) + var(--str-chat__font-family); + + // -------------------- + + // BODY + // ================================================================================= // + --str-chat__font-body-default: normal var(--typography-font-weight-regular) + var(--typography-font-size-md) / var(--typography-line-height-normal) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-body-emphasis: normal var(--typography-font-weight-semi-bold) + var(--typography-font-size-md) / var(--typography-line-height-normal) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-body-link: normal var(--typography-font-weight-regular) + var(--typography-font-size-md) / var(--typography-line-height-normal) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-body-link-emphasis: normal var(--typography-font-weight-semi-bold) + var(--typography-font-size-md) / var(--typography-line-height-normal) + var(--str-chat__font-family); + + // -------------------- + + // CAPTION + // ================================================================================= // + --str-chat__font-caption-default: normal var(--typography-font-weight-regular) + var(--typography-font-size-sm) / var(--typography-line-height-tight) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-caption-emphasis: normal var(--typography-font-weight-semi-bold) + var(--typography-font-size-sm) / var(--typography-line-height-tight) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-caption-link: normal var(--typography-font-weight-regular) + var(--typography-font-size-sm) / var(--typography-line-height-tight) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-caption-link-emphasis: normal var(--typography-font-weight-semi-bold) + var(--typography-font-size-sm) / var(--typography-line-height-tight) + var(--str-chat__font-family); + + // -------------------- + + // METADATA + // ================================================================================= // + --str-chat__font-metadata-default: normal var(--typography-font-weight-regular) + var(--typography-font-size-xs) / var(--typography-line-height-tight) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-metadata-emphasis: normal var(--typography-font-weight-semi-bold) + var(--typography-font-size-xs) / var(--typography-line-height-tight) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-metadata-link: normal var(--typography-font-weight-regular) + var(--typography-font-size-xs) / var(--typography-line-height-tight) + var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-metadata-link-emphasis: normal var(--typography-font-weight-semi-bold) + var(--typography-font-size-xs) / var(--typography-line-height-tight) + var(--str-chat__font-family); + + // -------------------- + + // NUMERIC + // ================================================================================= // + --str-chat__font-numeric-sm: normal var(--typography-font-weight-bold) + var(--typography-font-size-micro) / 100% var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-numeric-md: normal var(--typography-font-weight-bold) + var(--typography-font-size-xxs) / 100% var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-numeric-lg: normal var(--typography-font-weight-bold) + var(--typography-font-size-xs) / 100% var(--str-chat__font-family); + + // -------------------- + + --str-chat__font-numeric-xl: normal var(--typography-font-weight-bold) + var(--typography-font-size-sm) / var(--line-height-line-height-14) + var(--str-chat__font-family); + + // -------------------- +}