diff --git a/packages/pl-fe/src/columns/notifications.tsx b/packages/pl-fe/src/columns/notifications.tsx index 6b7c8ceea..dc8426ebf 100644 --- a/packages/pl-fe/src/columns/notifications.tsx +++ b/packages/pl-fe/src/columns/notifications.tsx @@ -83,7 +83,9 @@ const FilterBar = () => { }); } else { items.push({ - text: , + text: ( + + ), title: intl.formatMessage(messages.mentions), action: onClick('mention'), name: 'mention', @@ -94,6 +96,7 @@ const FilterBar = () => { ), title: intl.formatMessage(messages.statuses), @@ -101,13 +104,25 @@ const FilterBar = () => { name: 'status', }); items.push({ - text: , + text: ( + + ), title: intl.formatMessage(messages.favourites), action: onClick('favourite'), name: 'favourite', }); items.push({ - text: , + text: ( + + ), title: intl.formatMessage(messages.boosts), action: onClick('reblog'), name: 'reblog', @@ -115,7 +130,11 @@ const FilterBar = () => { if (features.polls) items.push({ text: ( - + ), title: intl.formatMessage(messages.polls), action: onClick('poll'), @@ -127,6 +146,7 @@ const FilterBar = () => { ), title: intl.formatMessage(messages.events), @@ -134,7 +154,13 @@ const FilterBar = () => { name: 'events', }); items.push({ - text: , + text: ( + + ), title: intl.formatMessage(messages.follows), action: onClick('follow'), name: 'follow', diff --git a/packages/pl-fe/src/components/alt-indicator.tsx b/packages/pl-fe/src/components/alt-indicator.tsx index 6076c617c..944bbe05b 100644 --- a/packages/pl-fe/src/components/alt-indicator.tsx +++ b/packages/pl-fe/src/components/alt-indicator.tsx @@ -20,7 +20,11 @@ const AltIndicator: React.FC = React.forwardRef {warning && ( - + )} {message ?? ( diff --git a/packages/pl-fe/src/components/media-gallery.tsx b/packages/pl-fe/src/components/media-gallery.tsx index 3f4de40bc..17b55e58a 100644 --- a/packages/pl-fe/src/components/media-gallery.tsx +++ b/packages/pl-fe/src/components/media-gallery.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; import React, { useState, useRef, useLayoutEffect, CSSProperties } from 'react'; -import { FormattedMessage } from 'react-intl'; +import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import AltIndicator from '@/components/alt-indicator'; import Blurhash from '@/components/blurhash'; @@ -30,6 +30,10 @@ import type { MediaAttachment } from 'pl-api'; const ATTACHMENT_LIMIT = 4; const MAX_FILENAME_LENGTH = 45; +const messages = defineMessages({ + altText: { id: 'media_gallery.alt_indicator', defaultMessage: 'Show image description' }, +}); + interface Dimensions { w: CSSProperties['width']; h: CSSProperties['height']; @@ -90,6 +94,7 @@ const Item: React.FC = ({ total, visible, }) => { + const intl = useIntl(); const { autoPlayGif } = useSettings(); const { mediaPreview } = useFrontendConfig(); @@ -231,6 +236,7 @@ const Item: React.FC = ({ } isFlush + title={intl.formatMessage(messages.altText)} > diff --git a/packages/pl-fe/src/components/status-reactions-bar.tsx b/packages/pl-fe/src/components/status-reactions-bar.tsx index 94b122306..d6cdcb9c5 100644 --- a/packages/pl-fe/src/components/status-reactions-bar.tsx +++ b/packages/pl-fe/src/components/status-reactions-bar.tsx @@ -150,7 +150,7 @@ const StatusReactionsBar: React.FC = ({ status, collapsed } className='⁂-status-reactions-bar__picker-button emoji-picker-dropdown' title={intl.formatMessage(messages.addEmoji)} > - + )} diff --git a/packages/pl-fe/src/components/status.tsx b/packages/pl-fe/src/components/status.tsx index f3a6996d5..da7330e32 100644 --- a/packages/pl-fe/src/components/status.tsx +++ b/packages/pl-fe/src/components/status.tsx @@ -40,6 +40,7 @@ import StatusInfo from './statuses/status-info'; import Tombstone from './tombstone'; const messages = defineMessages({ + edited: { id: 'status.edited', defaultMessage: 'Edited {date}' }, reblogged_by: { id: 'status.reblogged_by', defaultMessage: '{name} reposted' }, }); @@ -47,37 +48,49 @@ interface IAccountInfo { status: SelectedStatus; } -const AccountInfo: React.FC = React.memo(({ status }) => ( -
- { - event.stopPropagation(); - }} - > - - - - - {!!status.edited_at && ( - <> - - - = React.memo(({ status }) => { + const intl = useIntl(); + return ( +
+ { + event.stopPropagation(); + }} + > + - - )} -
-)); + + + + {!!status.edited_at && ( + <> + + + + + )} +
+ ); +}); interface IStatusFollowedTagInfo { status: SelectedStatus; diff --git a/packages/pl-fe/src/components/ui/popover.tsx b/packages/pl-fe/src/components/ui/popover.tsx index 0fafae3a9..d838f6216 100644 --- a/packages/pl-fe/src/components/ui/popover.tsx +++ b/packages/pl-fe/src/components/ui/popover.tsx @@ -31,6 +31,7 @@ interface IPopover { referenceElementClassName?: string; offsetOptions?: OffsetOptions; placements?: Array; + title?: string; } /** @@ -47,6 +48,7 @@ const Popover: React.FC = ({ isFlush = false, offsetOptions = 10, placements = ['top', 'bottom'], + title, }) => { const [isOpen, setIsOpen] = useState(false); @@ -95,7 +97,11 @@ const Popover: React.FC = ({ ref: refs.setReference, ...getReferenceProps(), className: clsx(children.props.className, referenceElementClassName), + 'aria-haspopup': interaction === 'click' ? 'dialog' : undefined, 'aria-expanded': isOpen, + role: interaction === 'click' ? 'button' : undefined, + tabIndex: interaction === 'click' ? 0 : undefined, + title: title || children.props.title, })} {isMounted && ( @@ -109,6 +115,7 @@ const Popover: React.FC = ({ left: x ?? 0, ...styles, }} + role='dialog' >
= ({ {onDescriptionChange && !description && ( - )} diff --git a/packages/pl-fe/src/features/account/components/header.tsx b/packages/pl-fe/src/features/account/components/header.tsx index b442af06a..16db231de 100644 --- a/packages/pl-fe/src/features/account/components/header.tsx +++ b/packages/pl-fe/src/features/account/components/header.tsx @@ -131,6 +131,7 @@ const messages = defineMessages({ notePlaceholder: { id: 'account_note.placeholder', defaultMessage: 'Add a note' }, noteSaved: { id: 'account_note.success', defaultMessage: 'Note saved' }, noteSaveFailed: { id: 'account_note.fail', defaultMessage: 'Failed to save note' }, + headerAlt: { id: 'account.header.alt.popover', defaultMessage: 'Show profile header alt text' }, }); interface IMovedNote { @@ -745,6 +746,7 @@ const Header: React.FC = ({ account }) => { } isFlush + title={intl.formatMessage(messages.headerAlt)} > = ({ statusId }) => { diff --git a/packages/pl-fe/src/features/edit-profile/components/header-picker.tsx b/packages/pl-fe/src/features/edit-profile/components/header-picker.tsx index 50ddb8693..ab4ac15ef 100644 --- a/packages/pl-fe/src/features/edit-profile/components/header-picker.tsx +++ b/packages/pl-fe/src/features/edit-profile/components/header-picker.tsx @@ -21,6 +21,11 @@ const messages = defineMessages({ defaultMessage: 'Image description', }, changeHeaderDescriptionConfirm: { id: 'group.upload_banner.alt.confirm', defaultMessage: 'Save' }, + clearHeader: { id: 'group.upload_banner.clear', defaultMessage: 'Clear header image' }, + changeDescription: { + id: 'group.upload_banner.change_description', + defaultMessage: 'Change alt text', + }, }); interface IMediaInput { @@ -111,6 +116,7 @@ const HeaderPicker = React.forwardRef( theme='dark' className='absolute right-2 top-2 z-10 hover:scale-105 hover:bg-gray-900' iconClassName='h-5 w-5' + title={intl.formatMessage(messages.clearHeader)} /> )} {onChangeDescription && src && ( @@ -118,6 +124,7 @@ const HeaderPicker = React.forwardRef( type='button' className='absolute left-2 top-2' onClick={handleChangeDescriptionClick} + title={intl.formatMessage(messages.changeDescription)} > diff --git a/packages/pl-fe/src/locales/en.json b/packages/pl-fe/src/locales/en.json index 7f0c2ea04..523dab32f 100644 --- a/packages/pl-fe/src/locales/en.json +++ b/packages/pl-fe/src/locales/en.json @@ -34,6 +34,7 @@ "account.follows.empty": "This user doesn't follow anyone yet.", "account.follows_you": "Follows you", "account.header.alt": "Profile header", + "account.header.alt.popover": "Show profile header alt text", "account.header.description": "Header description", "account.hide_reblogs": "Hide reposts from @{name}", "account.instance_favicon": "Visit {domain} timeline", @@ -552,6 +553,7 @@ "compose_event.fields.name_placeholder": "Name", "compose_event.fields.start_time_label": "Event start date", "compose_event.fields.start_time_placeholder": "Event begins on…", + "compose_event.header_description": "Add header alt text.", "compose_event.participation_requests.authorize": "Authorize", "compose_event.participation_requests.authorize.fail": "Failed to authorize event participation request", "compose_event.participation_requests.authorize.success": "Event participation request authorized successfully", @@ -567,6 +569,7 @@ "compose_event.update": "Update", "compose_event.upload_banner": "Upload event banner", "compose_form.approval_required": "The reply needs to be approved by the post author.", + "compose_form.approval_required.quote": "The quote needs to be approved by the post author.", "compose_form.content_type.change": "Change content type", "compose_form.direct_message_warning": "This post will only be sent to the mentioned users.", "compose_form.drive_button": "Select from drive", @@ -1060,6 +1063,8 @@ "group.upload_banner.alt.confirm": "Save", "group.upload_banner.alt.heading": "Change header description", "group.upload_banner.alt.placeholder": "Image description", + "group.upload_banner.change_description": "Change alt text", + "group.upload_banner.clear": "Clear header image", "group.upload_banner.title": "Upload background picture", "groups.discover.search.results.member_count": "{members, plural, one {member} other {members}}", "groups.empty.subtitle": "Start discovering groups to join or create your own.", @@ -1276,6 +1281,7 @@ "media.default_description.gifv": "GIFV", "media.default_description.image": "Image", "media.default_description.video": "Video", + "media_gallery.alt_indicator": "Show image description", "media_panel.empty_message": "No media found.", "media_panel.title": "Media", "mfa.confirm.success_message": "MFA confirmed",