Name the services Connect your data can sync

The tile names up to three services this instance can sync (then "and
more"), rather than services it may not offer, and spans the grid's
width so its line doesn't wrap into a tall tile.
This commit is contained in:
arc53-machine committed 2026-09-29 18:40:19 +01:00
1 parent 4468e19fb8
commit 6ccc506b8c
9 files changed
+68 -16

No files matched your search

+2 -1
View File
@@ -1815,7 +1815,8 @@
},
"connectData": {
"title": "Deine Daten verbinden",
"description": "Google Drive, SharePoint, Confluence und mehr synchronisieren"
"description": "{{services}} synchronisieren",
"more": "mehr"
}
},
"createAPIKey": {
+2 -1
View File
@@ -1821,7 +1821,8 @@
},
"connectData": {
"title": "Connect your data",
"description": "Sync Google Drive, SharePoint, Confluence and more"
"description": "Sync {{services}}",
"more": "more"
}
},
"createAPIKey": {
+2 -1
View File
@@ -1815,7 +1815,8 @@
},
"connectData": {
"title": "Conecta tus datos",
"description": "Sincroniza Google Drive, SharePoint, Confluence y más"
"description": "Sincroniza {{services}}",
"more": "más"
}
},
"createAPIKey": {
+2 -1
View File
@@ -1804,7 +1804,8 @@
},
"connectData": {
"title": "データを接続",
"description": "Google Drive、SharePoint、Confluence などを同期"
"description": "{{services}} を同期",
"more": "その他"
}
},
"createAPIKey": {
+2 -1
View File
@@ -1899,7 +1899,8 @@
},
"connectData": {
"title": "Подключите свои данные",
"description": "Синхронизируйте Google Drive, SharePoint, Confluence и другие сервисы"
"description": "Синхронизируйте {{services}}",
"more": "другие сервисы"
}
},
"createAPIKey": {
+2 -1
View File
@@ -1804,7 +1804,8 @@
},
"connectData": {
"title": "連接你的資料",
"description": "同步 Google Drive、SharePoint、Confluence 等"
"description": "同步 {{services}}",
"more": "更多服務"
}
},
"createAPIKey": {
+2 -1
View File
@@ -1804,7 +1804,8 @@
},
"connectData": {
"title": "连接你的数据",
"description": "同步 Google Drive、SharePoint、Confluence 等"
"description": "同步 {{services}}",
"more": "更多服务"
}
},
"createAPIKey": {
+27 -2
View File
@@ -2,7 +2,11 @@ import { act } from 'react';
import { createRoot, type Root } from 'react-dom/client';
vi.mock('react-i18next', () => ({
useTranslation: () => ({ t: (key: string) => key }),
useTranslation: () => ({
t: (key: string, opts?: { services?: string }) =>
opts?.services ? `${key}(${opts.services})` : key,
i18n: { language: 'en' },
}),
}));
const connectorsState = vi.hoisted(() => ({
@@ -136,6 +140,7 @@ describe('Upload source-type tiles', () => {
const DRIVE = {
key: 'google_drive',
name: 'Google Drive',
icon: 'drive',
sync_ingestor: 'google_drive',
capabilities: ['sync'],
@@ -168,11 +173,30 @@ describe('Upload source-type tiles', () => {
false,
);
expect(labels.at(-1)).toContain('modals.uploadDoc.connectData.title');
expect(labels.at(-1)).toContain('modals.uploadDoc.connectData.description');
// Named after what this instance can sync.
expect(labels.at(-1)).toContain(
'modals.uploadDoc.connectData.description(Google Drive)',
);
expect(document.body.querySelector('h3')).toBeNull();
connectorsState.catalog = [];
});
it('names three syncing services, then the rest as more', async () => {
connectorsState.catalog = [
DRIVE,
{ ...DRIVE, key: 'share_point', name: 'SharePoint' },
{ ...DRIVE, key: 'confluence', name: 'Confluence' },
{ ...DRIVE, key: 's3', name: 'Amazon S3' },
{ ...DRIVE, key: 'off', name: 'Needs setup', available: false },
{ key: 'telegram', name: 'Telegram', capabilities: ['write'] },
];
await render();
expect(connectTile()!.textContent).toContain(
'modals.uploadDoc.connectData.description(Google Drive, SharePoint, Confluence, and modals.uploadDoc.connectData.more)',
);
connectorsState.catalog = [];
});
it('opens the Connectors page on the syncing services', async () => {
connectorsState.catalog = [DRIVE];
navigate.mockClear();
@@ -190,6 +214,7 @@ describe('Upload source-type tiles', () => {
it('offers no Connect tile when no service can sync', async () => {
connectorsState.catalog = [
{ key: 'telegram', capabilities: ['write'], available: true },
{ ...DRIVE, available: false },
];
await render();
expect(connectTile()).toBeUndefined();
+27 -7
View File
@@ -29,6 +29,8 @@ import { Separator } from '../components/ui/separator';
import { OptionCard } from '../components/ui/option-card';
import ConnectorSetupNotice from '../connectors/ConnectorSetupNotice';
import useConnectorLauncher from '../connectors/useConnectorLauncher';
import { connectorName } from '../connectors/i18n';
import { intlLocale } from '../utils/dateTimeUtils';
import {
loadConnectors,
selectConnections,
@@ -474,7 +476,7 @@ function Upload({
}));
const [nameTouched, setNameTouched] = useState(false);
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const dispatch = useDispatch<AppDispatch>();
const store = useStore<RootState>();
@@ -1152,10 +1154,27 @@ function Upload({
};
// Services that sync into Knowledge are connected on the Connectors page,
// so the picker offers one way there instead of a tile per service.
const offersConnect =
connectorsEnabled &&
connectorCatalog.some((c) => c.capabilities?.includes('sync'));
// so the picker offers one way there instead of a tile per service, named
// after the first few this instance can sync.
const syncServices = connectorsEnabled
? connectorCatalog
.filter((c) => c.available && c.capabilities?.includes('sync'))
.map((c) => connectorName(t, c))
: [];
const SERVICES_NAMED = 3;
const connectDescription = t('modals.uploadDoc.connectData.description', {
services: new Intl.ListFormat(intlLocale(i18n.language), {
type: 'conjunction',
}).format(
syncServices.length > SERVICES_NAMED
? [
...syncServices.slice(0, SERVICES_NAMED),
t('modals.uploadDoc.connectData.more'),
]
: syncServices,
),
interpolation: { escapeValue: false },
});
const renderIngestorSelection = () => {
const options = UPLOAD_AND_WEB_INGESTORS.map((type) =>
@@ -1173,11 +1192,12 @@ function Upload({
onClick={() => handleIngestorTypeChange(option.value)}
/>
))}
{offersConnect && (
{syncServices.length > 0 && (
<OptionCard
className="sm:col-span-2 md:col-span-3"
icon={<Plug />}
title={t('modals.uploadDoc.connectData.title')}
description={t('modals.uploadDoc.connectData.description')}
description={connectDescription}
onClick={() => {
handleClose();
navigate('/settings/connectors?capability=sync');