fix: Various accessibility issues (#10115)

* Round 1

* Round 2

* Shared page
This commit is contained in:
Tom Moor
2025-09-07 14:36:35 +02:00
committed by GitHub
parent 0bde1d5ef4
commit 58a41a6fde
36 changed files with 138 additions and 35 deletions
+3 -2
View File
@@ -53,9 +53,10 @@ const ActionButton = React.forwardRef<HTMLButtonElement, Props>(
}
const label =
typeof action.name === "function"
rest["aria-label"] ??
(typeof action.name === "function"
? action.name(actionContext)
: action.name;
: action.name);
const button = (
<button
+4 -1
View File
@@ -25,6 +25,8 @@ type Props = {
onClick?: React.MouseEventHandler<HTMLImageElement>;
/** Size of the avatar, defaults to AvatarSize.Large */
size?: AvatarSize;
/** Optional alt text for the avatar image */
alt?: string;
/** Optional inline styles to apply to the avatar wrapper */
style?: React.CSSProperties;
};
@@ -53,6 +55,7 @@ function AvatarWithPresence({
isCurrentUser,
size = AvatarSize.Large,
style,
alt,
}: Props) {
const { t } = useTranslation();
const status = isPresent
@@ -83,7 +86,7 @@ function AvatarWithPresence({
$color={user.color}
style={style}
>
<Avatar model={user} onClick={onClick} size={size} />
<Avatar model={user} onClick={onClick} size={size} alt={alt} />
</AvatarPresence>
</Tooltip>
</>
+1
View File
@@ -132,6 +132,7 @@ function Collaborators(props: Props) {
isEditing={isEditing}
isObserving={isObserving}
isCurrentUser={currentUserId === collaborator.id}
alt={t("Avatar of {{ name }}", { name: collaborator.name })}
onClick={
isObservable
? handleAvatarClick(
+3 -2
View File
@@ -143,13 +143,14 @@ const ContentEditable = React.forwardRef(function _ContentEditable(
},
[]
);
const contentEditable = !disabled && !readOnly;
return (
<div className={className} dir={dir} onClick={onClick} tabIndex={-1}>
{children}
<Content
ref={contentRef}
contentEditable={!disabled && !readOnly}
contentEditable={contentEditable}
onInput={wrappedEvent(onInput)}
onFocus={wrappedEvent(onFocus)}
onBlur={wrappedEvent(onBlur)}
@@ -157,7 +158,7 @@ const ContentEditable = React.forwardRef(function _ContentEditable(
onPaste={handlePaste}
data-placeholder={placeholder}
suppressContentEditableWarning
role="textbox"
role={contentEditable ? "textbox" : undefined}
{...rest}
>
{innerValue}
@@ -1,5 +1,6 @@
import { MoreIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { MenuButton } from "reakit/Menu";
import NudeButton from "~/components/NudeButton";
@@ -8,10 +9,16 @@ type Props = React.ComponentProps<typeof MenuButton> & {
};
export default function OverflowMenuButton({ className, ...rest }: Props) {
const { t } = useTranslation();
return (
<MenuButton {...rest}>
{(props) => (
<NudeButton className={className} {...props}>
<NudeButton
className={className}
aria-label={t("More options")}
{...props}
>
<MoreIcon />
</NudeButton>
)}
+2 -2
View File
@@ -114,7 +114,6 @@ function DocumentListItem(
<DocumentLink
ref={itemRef}
dir={document.dir}
role="menuitem"
$isStarred={document.isStarred}
$menuOpen={menuOpen}
to={{
@@ -279,7 +278,7 @@ const DocumentLink = styled(Link)<{
`}
`;
const Heading = styled.h3<{ rtl?: boolean }>`
const Heading = styled.span<{ rtl?: boolean }>`
display: flex;
justify-content: ${(props) => (props.rtl ? "flex-end" : "flex-start")};
align-items: center;
@@ -289,6 +288,7 @@ const Heading = styled.h3<{ rtl?: boolean }>`
color: ${s("text")};
font-family: ${s("fontFamily")};
font-weight: 500;
font-size: 20px;
`;
const StarPositioner = styled(Flex)`
+1 -7
View File
@@ -168,13 +168,7 @@ const DocumentMeta: React.FC<Props> = ({
};
return (
<Container
align="center"
rtl={document.dir === "rtl"}
{...rest}
dir="ltr"
lang=""
>
<Container align="center" rtl={document.dir === "rtl"} {...rest} dir="ltr">
{to ? (
<Link to={to} replace={replace}>
{content}
@@ -67,7 +67,11 @@ function Notifications(
<Flex gap={8}>
{notifications.approximateUnreadCount > 0 && (
<Tooltip content={t("Mark all as read")}>
<Button action={markNotificationsAsRead} context={context}>
<Button
action={markNotificationsAsRead}
context={context}
aria-label={t("Mark all as read")}
>
<MarkAsReadIcon />
</Button>
</Tooltip>
@@ -63,6 +63,7 @@ export const OAuthClientForm = observer(function OAuthClientForm_({
name="avatarUrl"
render={({ field }) => (
<ImageInput
alt={t("OAuth client icon")}
onSuccess={(url) => field.onChange(url)}
onError={(err) => setError("avatarUrl", { message: err })}
model={{
+1
View File
@@ -255,6 +255,7 @@ const PaginatedList = <T extends PaginatedItem>({
<React.Fragment>
{heading}
<ArrowKeyNavigation
role={rest.role}
aria-label={rest["aria-label"]}
onEscape={onEscape}
className={className}
+4
View File
@@ -168,6 +168,7 @@ function SearchPopover({ shareId, className }: Props) {
<Popover open={open} onOpenChange={setOpen} modal={true}>
<PopoverAnchor>
<StyledInputSearch
role="combobox"
aria-controls="search-results"
aria-expanded={open}
aria-haspopup="listbox"
@@ -176,6 +177,8 @@ function SearchPopover({ shareId, className }: Props) {
onFocus={handleSearchInputFocus}
onKeyDown={handleKeyDown}
className={className}
label={t("Search")}
labelHidden
/>
</PopoverAnchor>
<PopoverContent
@@ -194,6 +197,7 @@ function SearchPopover({ shareId, className }: Props) {
}}
>
<PaginatedList<SearchResult>
role="listbox"
options={{ query, snippetMinWords: 10, snippetMaxWords: 11 }}
items={cachedSearchResults}
fetch={performSearch}
@@ -25,6 +25,11 @@ export const AppearanceAction = observer(() => {
onClick={() =>
ui.setTheme(resolvedTheme === "light" ? Theme.Dark : Theme.Light)
}
aria-label={
resolvedTheme === "light"
? t("Switch to dark")
: t("Switch to light")
}
neutral
borderOnHover
/>
+5
View File
@@ -81,6 +81,11 @@ function AppSidebar() {
<ToggleButton
position="bottom"
image={<SidebarIcon />}
aria-label={
ui.sidebarCollapsed
? t("Expand sidebar")
: t("Collapse sidebar")
}
onClick={() => {
ui.toggleCollapsedSidebar();
(document.activeElement as HTMLElement)?.blur();
+3
View File
@@ -52,6 +52,9 @@ function SettingsSidebar() {
>
<Tooltip content={t("Toggle sidebar")} shortcut={`${metaDisplay}+.`}>
<ToggleButton
aria-label={
ui.sidebarCollapsed ? t("Expand sidebar") : t("Collapse sidebar")
}
position="bottom"
image={<SidebarIcon />}
onClick={() => {
+3
View File
@@ -96,6 +96,9 @@ const ToggleSidebar = () => {
<ToggleButton
position="bottom"
image={<SidebarIcon />}
aria-label={
ui.sidebarCollapsed ? t("Expand sidebar") : t("Collapse sidebar")
}
onClick={() => {
ui.toggleCollapsedSidebar();
(document.activeElement as HTMLElement)?.blur();
+8 -2
View File
@@ -21,6 +21,7 @@ import { TooltipProvider } from "../TooltipContext";
import ResizeBorder from "./components/ResizeBorder";
import SidebarButton from "./components/SidebarButton";
import ToggleButton from "./components/ToggleButton";
import { useTranslation } from "react-i18next";
const ANIMATION_MS = 250;
@@ -35,6 +36,7 @@ const Sidebar = React.forwardRef<HTMLDivElement, Props>(function _Sidebar(
ref: React.RefObject<HTMLDivElement>
) {
const [isCollapsing, setCollapsing] = React.useState(false);
const { t } = useTranslation();
const theme = useTheme();
const { ui } = useStores();
const location = useLocation();
@@ -237,7 +239,7 @@ const Sidebar = React.forwardRef<HTMLDivElement, Props>(function _Sidebar(
position="bottom"
image={
<Avatar
alt={user.name}
alt={t("Avatar of {{ name} }", { name: user.name })}
model={user}
size={24}
style={{ marginLeft: 4 }}
@@ -245,7 +247,11 @@ const Sidebar = React.forwardRef<HTMLDivElement, Props>(function _Sidebar(
}
>
<NotificationsPopover>
<SidebarButton position="bottom" image={<NotificationIcon />} />
<SidebarButton
position="bottom"
image={<NotificationIcon />}
aria-label={t("Notifications")}
/>
</NotificationsPopover>
</SidebarButton>
</AccountMenu>
@@ -150,6 +150,7 @@ const CollectionLink: React.FC<Props> = ({
{can.createDocument && (
<NudeButton
tooltip={{ content: t("New doc"), delay: 500 }}
aria-label={t("New nested document")}
onClick={(ev) => {
ev.preventDefault();
setIsAddingNewChild();
@@ -364,7 +364,6 @@ function InnerDocumentLink(
{can.createChildDocument && (
<Tooltip content={t("New doc")}>
<NudeButton
type={undefined}
aria-label={t("New nested document")}
onClick={(ev) => {
ev.preventDefault();
@@ -1,3 +1,4 @@
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
import invariant from "invariant";
import { observer } from "mobx-react";
import { useCallback } from "react";
@@ -61,7 +62,12 @@ function DropToImport({ disabled, children, collectionId, documentId }: Props) {
$isDragActive={isDragActive}
tabIndex={-1}
>
<input {...getInputProps()} />
<VisuallyHidden>
<label>
{t("Import files")}
<input {...getInputProps()} />
</label>
</VisuallyHidden>
{isImporting && <LoadingIndicator />}
{children}
</DropzoneContainer>
+6 -4
View File
@@ -1,7 +1,7 @@
import { CollapsedIcon } from "outline-icons";
import * as React from "react";
import styled, { keyframes } from "styled-components";
import { s } from "@shared/styles";
import { extraArea, s } from "@shared/styles";
import usePersistedState from "~/hooks/usePersistedState";
import { undraggableOnDesktop } from "~/styles";
@@ -71,17 +71,18 @@ const Button = styled.button`
font-size: 13px;
font-weight: 600;
user-select: none;
color: ${s("textTertiary")};
color: ${s("sidebarText")};
position: relative;
letter-spacing: 0.03em;
margin: 0;
padding: 4px 2px 4px 12px;
height: 22px;
border: 0;
background: none;
border-radius: 4px;
-webkit-appearance: none;
transition: all 100ms ease;
${undraggableOnDesktop()}
${extraArea(4)}
&:not(:disabled):hover,
&:not(:disabled):active {
@@ -102,7 +103,8 @@ const Disclosure = styled(CollapsedIcon)<{ expanded?: boolean }>`
const H3 = styled.h3`
margin: 0;
&:hover {
&:hover,
&:focus-within {
${Disclosure} {
opacity: 1;
}
+14 -3
View File
@@ -347,6 +347,7 @@ export default function FindAndReplace({
<ButtonLarge
disabled={disabled}
onClick={() => editor.commands.prevSearchMatch()}
aria-label={t("Previous match")}
>
<CaretUpIcon />
</ButtonLarge>
@@ -355,6 +356,7 @@ export default function FindAndReplace({
<ButtonLarge
disabled={disabled}
onClick={() => editor.commands.nextSearchMatch()}
aria-label={t("Next match")}
>
<CaretDownIcon />
</ButtonLarge>
@@ -390,7 +392,10 @@ export default function FindAndReplace({
shortcut={`${altDisplay}+${metaDisplay}+c`}
placement="bottom"
>
<ButtonSmall onClick={handleCaseSensitive}>
<ButtonSmall
onClick={handleCaseSensitive}
aria-label={t("Match case")}
>
<CaseSensitiveIcon
color={caseSensitive ? theme.accent : theme.textSecondary}
/>
@@ -401,7 +406,10 @@ export default function FindAndReplace({
shortcut={`${altDisplay}+${metaDisplay}+r`}
placement="bottom"
>
<ButtonSmall onClick={handleRegex}>
<ButtonSmall
onClick={handleRegex}
aria-label={t("Enable regex")}
>
<RegexIcon
color={regexEnabled ? theme.accent : theme.textSecondary}
/>
@@ -416,7 +424,10 @@ export default function FindAndReplace({
shortcut={`${altDisplay}+${metaDisplay}+f`}
placement="bottom"
>
<ButtonLarge onClick={handleMore}>
<ButtonLarge
onClick={handleMore}
aria-label={t("Replace options")}
>
<ReplaceIcon color={theme.textSecondary} />
</ButtonLarge>
</Tooltip>
+7 -1
View File
@@ -7,6 +7,7 @@ import { EditorStyleHelper } from "@shared/editor/styles/EditorStyleHelper";
import { extraArea } from "@shared/styles";
import Input, { NativeInput, Outline } from "~/components/Input";
import { useEditor } from "./EditorContext";
import { useTranslation } from "react-i18next";
type Dimension = {
width: string;
@@ -20,6 +21,7 @@ export function MediaDimension() {
width: { min: number; max: number };
height: { min: number; max: number };
}>();
const { t } = useTranslation();
const { view, commands } = useEditor();
const { state } = view;
const { selection } = state;
@@ -205,6 +207,8 @@ export function MediaDimension() {
return (
<StyledFlex ref={ref} align="center">
<StyledInput
label={t("Image width")}
labelHidden
value={localDimension.width}
onChange={handleChange("width")}
onBlur={handleBlur}
@@ -212,9 +216,11 @@ export function MediaDimension() {
$error={error.width}
/>
<Text size="xsmall" type="tertiary">
x
×
</Text>
<StyledInput
label={t("Image height")}
labelHidden
value={localDimension.height}
onChange={handleChange("height")}
onBlur={handleBlur}
+6 -1
View File
@@ -64,7 +64,11 @@ function ToolbarDropdown(props: { active: boolean; item: MenuItem }) {
<>
<MenuButton {...menu}>
{(buttonProps) => (
<ToolbarButton {...buttonProps} hovering={menu.visible}>
<ToolbarButton
{...buttonProps}
hovering={menu.visible}
aria-label={item.tooltip}
>
{item.label && <Label>{item.label}</Label>}
{item.icon}
</ToolbarButton>
@@ -118,6 +122,7 @@ function ToolbarMenu(props: Props) {
<ToolbarButton
onClick={handleClick(item)}
active={isActive && !item.label}
aria-label={item.label ? undefined : item.tooltip}
>
{item.label && <Label>{item.label}</Label>}
{item.icon}
+1
View File
@@ -498,6 +498,7 @@ export class Editor extends React.PureComponent<
// Tell third-party libraries and screen-readers that this is an input
view.dom.setAttribute("role", "textbox");
view.dom.setAttribute("aria-label", "Editor content");
return view;
}
+1 -1
View File
@@ -35,7 +35,7 @@ export default function useDictionary() {
deleteRow: t("Delete"),
deleteTable: t("Delete table"),
deleteAttachment: t("Delete file"),
dimensions: t("Width x Height"),
dimensions: `${t("Width")} × ${t("Height")}`,
download: t("Download"),
downloadAttachment: t("Download file"),
replaceAttachment: t("Replace file"),
+1 -1
View File
@@ -20,7 +20,7 @@ const NotificationMenu: React.FC = () => {
return (
<DropdownMenu action={rootAction} ariaLabel={t("Notifications")}>
<Button>
<Button aria-label={t("Notifications")}>
<MoreIcon />
</Button>
</DropdownMenu>
@@ -23,6 +23,7 @@ import { useDocumentContext } from "~/components/DocumentContext";
import { PopoverButton } from "~/components/IconPicker/components/PopoverButton";
import useBoolean from "~/hooks/useBoolean";
import usePolicy from "~/hooks/usePolicy";
import { useTranslation } from "react-i18next";
const IconPicker = React.lazy(() => import("~/components/IconPicker"));
@@ -70,6 +71,7 @@ const DocumentTitle = React.forwardRef(function _DocumentTitle(
}: Props,
externalRef: React.RefObject<RefHandle>
) {
const { t } = useTranslation();
const ref = React.useRef<RefHandle>(null);
const [iconPickerIsOpen, handleOpen, setIconPickerClosed] = useBoolean();
const { editor } = useDocumentContext();
@@ -249,6 +251,7 @@ const DocumentTitle = React.forwardRef(function _DocumentTitle(
autoFocus={!title}
maxLength={DocumentValidation.maxTitleLength}
readOnly={readOnly}
aria-label={t("Document title")}
dir="auto"
ref={mergeRefs([ref, externalRef])}
>
@@ -134,6 +134,7 @@ function DocumentHeader({
placement="bottom"
>
<Button
aria-label={t("Show contents")}
onClick={handleToggle}
icon={<TableOfContentsIcon />}
borderOnHover
@@ -23,7 +23,11 @@ function KeyboardShortcutsButton() {
return (
<Tooltip content={t("Keyboard shortcuts")} shortcut="?">
<Button onClick={handleOpenKeyboardShortcuts} $hidden={isEditingFocus}>
<Button
onClick={handleOpenKeyboardShortcuts}
$hidden={isEditingFocus}
aria-label={t("Keyboard shortcuts")}
>
<KeyboardIcon />
</Button>
</Tooltip>
+1
View File
@@ -161,6 +161,7 @@ const Application = observer(function Application({ oauthClient }: Props) {
name="avatarUrl"
render={({ field }) => (
<ImageInput
alt={t("Application icon")}
onSuccess={(url) => field.onChange(url)}
onError={(err) => setError("avatarUrl", { message: err })}
model={{
+1
View File
@@ -193,6 +193,7 @@ function Details() {
)}
>
<ImageInput
alt={t("Workspace logo")}
onSuccess={handleAvatarChange}
onError={handleAvatarError}
model={team}
+1
View File
@@ -72,6 +72,7 @@ const Profile = () => {
description={t("Choose a photo or image to represent yourself.")}
>
<ImageInput
alt={t("Profile picture")}
onSuccess={handleAvatarChange}
onError={handleAvatarError}
model={user}
@@ -10,9 +10,10 @@ import ImageUpload, { Props as ImageUploadProps } from "./ImageUpload";
type Props = ImageUploadProps & {
model: IAvatar;
alt: string;
};
export default function ImageInput({ model, onSuccess, ...rest }: Props) {
export default function ImageInput({ model, onSuccess, alt, ...rest }: Props) {
const { t } = useTranslation();
return (
@@ -27,6 +28,7 @@ export default function ImageInput({ model, onSuccess, ...rest }: Props) {
model={model}
size={AvatarSize.Upload}
variant={AvatarVariant.Square}
alt={alt}
/>
<Flex auto align="center" justify="center" className="upload">
<EditIcon />
+3
View File
@@ -2,6 +2,7 @@ import * as React from "react";
import styled from "styled-components";
import { s } from "../../styles";
import { EditorStyleHelper } from "../styles/EditorStyleHelper";
import { useTranslation } from "react-i18next";
type Props = {
/** Callback triggered when the caption is blurred */
@@ -23,6 +24,7 @@ type Props = {
* A component that renders a caption for an image or video.
*/
function Caption({ placeholder, children, isSelected, width, ...rest }: Props) {
const { t } = useTranslation();
const handlePaste = (event: React.ClipboardEvent<HTMLParagraphElement>) => {
event.preventDefault();
const text = event.clipboardData.getData("text/plain");
@@ -42,6 +44,7 @@ function Caption({ placeholder, children, isSelected, width, ...rest }: Props) {
onPaste={handlePaste}
className={EditorStyleHelper.imageCaption}
tabIndex={-1}
aria-label={t("Caption")}
role="textbox"
contentEditable
suppressContentEditableWarning
+4 -1
View File
@@ -9,6 +9,7 @@ import toggleCheckboxItem from "../commands/toggleCheckboxItem";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import checkboxRule from "../rules/checkboxes";
import Node from "./Node";
import { v4 } from "uuid";
export default class CheckboxItem extends Node {
get name() {
@@ -34,6 +35,7 @@ export default class CheckboxItem extends Node {
},
],
toDOM: (node) => {
const id = `checkbox-${v4()}`;
const checked = node.attrs.checked.toString();
let input;
if (typeof document !== "undefined") {
@@ -41,6 +43,7 @@ export default class CheckboxItem extends Node {
input.tabIndex = -1;
input.className = "checkbox";
input.setAttribute("aria-checked", checked);
input.setAttribute("aria-labelledby", id);
input.setAttribute("role", "checkbox");
input.addEventListener("click", this.handleClick);
}
@@ -60,7 +63,7 @@ export default class CheckboxItem extends Node {
? [input]
: [["span", { class: "checkbox", "aria-checked": checked }]]),
],
["div", 0],
["div", { id }, 0],
];
},
};
+15 -1
View File
@@ -175,6 +175,7 @@
"currently viewing": "currently viewing",
"previously edited": "previously edited",
"You": "You",
"Avatar of {{ name }}": "Avatar of {{ name }}",
"Viewers": "Viewers",
"Collections are used to group documents and choose permissions": "Collections are used to group documents and choose permissions",
"Name": "Name",
@@ -205,6 +206,7 @@
"Move document": "Move document",
"Moving": "Moving",
"Moving the document <em>{{ title }}</em> to the {{ newCollectionName }} collection will change permission for all workspace members from <em>{{ prevPermission }}</em> to <em>{{ newPermission }}</em>.": "Moving the document <em>{{ title }}</em> to the {{ newCollectionName }} collection will change permission for all workspace members from <em>{{ prevPermission }}</em> to <em>{{ newPermission }}</em>.",
"More options": "More options",
"Submenu": "Submenu",
"Collections could not be loaded, please reload the app": "Collections could not be loaded, please reload the app",
"Start view": "Start view",
@@ -329,6 +331,7 @@
"Mark all as read": "Mark all as read",
"You're all caught up": "You're all caught up",
"Icon": "Icon",
"OAuth client icon": "OAuth client icon",
"My App": "My App",
"Tagline": "Tagline",
"A short description": "A short description",
@@ -405,12 +408,15 @@
"{{ count }} groups added to the document": "{{ count }} groups added to the document",
"{{ count }} groups added to the document_plural": "{{ count }} groups added to the document",
"Logo": "Logo",
"Expand sidebar": "Expand sidebar",
"Collapse sidebar": "Collapse sidebar",
"Archived collections": "Archived collections",
"New doc": "New doc",
"Empty": "Empty",
"Collapse": "Collapse",
"Expand": "Expand",
"Document not supported try Markdown, Plain text, HTML, or Word": "Document not supported try Markdown, Plain text, HTML, or Word",
"Import files": "Import files",
"Go back": "Go back",
"Go forward": "Go forward",
"Could not load shared documents": "Could not load shared documents",
@@ -427,6 +433,7 @@
"The {{ documentName }} cannot be moved here": "The {{ documentName }} cannot be moved here",
"Return to App": "Back to App",
"Installation": "Installation",
"Avatar of {{ name} }": "Avatar of {{ name} }",
"Unstar document": "Unstar document",
"Star document": "Star document",
"Template created, go ahead and customize it": "Template created, go ahead and customize it",
@@ -460,6 +467,8 @@
"Replacement": "Replacement",
"Replace": "Replace",
"Replace all": "Replace all",
"Image width": "Image width",
"Image height": "Image height",
"Profile picture": "Profile picture",
"Create a new doc": "Create a new doc",
"{{ userName }} won't be notified, as they do not have access to this document": "{{ userName }} won't be notified, as they do not have access to this document",
@@ -486,7 +495,8 @@
"Create a new child doc": "Create a new child doc",
"Delete table": "Delete table",
"Delete file": "Delete file",
"Width x Height": "Width x Height",
"Width": "Width",
"Height": "Height",
"Download file": "Download file",
"Replace file": "Replace file",
"Delete image": "Delete image",
@@ -680,6 +690,7 @@
"only you": "only you",
"person": "person",
"people": "people",
"Document title": "Document title",
"Last updated": "Last updated",
"Type '/' to insert, or start writing…": "Type '/' to insert, or start writing…",
"Hide contents": "Hide contents",
@@ -924,6 +935,7 @@
"Rotate secret": "Rotate secret",
"Rotating the client secret will invalidate the current secret. Make sure to update any applications using these credentials.": "Rotating the client secret will invalidate the current secret. Make sure to update any applications using these credentials.",
"Displayed to users when authorizing": "Displayed to users when authorizing",
"Application icon": "Application icon",
"Developer information shown to users when authorizing": "Developer information shown to users when authorizing",
"Developer name": "Developer name",
"Developer URL": "Developer URL",
@@ -1030,6 +1042,7 @@
"These settings affect the way that your workspace appears to everyone on the team.": "These settings affect the way that your workspace appears to everyone on the team.",
"Display": "Display",
"The logo is displayed at the top left of the application.": "The logo is displayed at the top left of the application.",
"Workspace logo": "Workspace logo",
"The workspace name, usually the same as your company name.": "The workspace name, usually the same as your company name.",
"Description": "Description",
"A short description of your workspace.": "A short description of your workspace.",
@@ -1260,6 +1273,7 @@
"{{ user }} updated {{ timeAgo }}": "{{ user }} updated {{ timeAgo }}",
"You created {{ timeAgo }}": "You created {{ timeAgo }}",
"{{ user }} created {{ timeAgo }}": "{{ user }} created {{ timeAgo }}",
"Caption": "Caption",
"Open": "Open",
"Error loading data": "Error loading data"
}