chore: improve coverage of cypress (#5483)

This commit is contained in:
Kilu.He 2024-06-06 19:47:14 +08:00 committed by GitHub
parent 3b72f90ca5
commit b794f3894e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 408 additions and 147 deletions

File diff suppressed because one or more lines are too long

View File

@ -1 +1,80 @@
[{"database_id":"037a985f-f369-4c4a-8011-620012850a68","created_at":"1713429700","views":["48c52cf7-bf98-43fa-96ad-b31aade9b071"]},{"database_id":"daea6aee-9365-4703-a8e2-a2fa6a07b214","created_at":"1714449533","views":["b6347acb-3174-4f0e-98e9-dcce07e5dbf7"]},{"database_id":"4c658817-20db-4f56-b7f9-0637a22dfeb6","created_at":"0","views":["7d2148fc-cace-4452-9c5c-96e52e6bf8b5","e410747b-5f2f-45a0-b2f7-890ad3001355","2143e95d-5dcb-4e0f-bb2c-50944e6e019f","a5566e49-f156-4168-9b2d-17926c5da329","135615fa-66f7-4451-9b54-d7e99445fca4","b4e77203-5c8b-48df-bbc5-2e1143eb0e61","a6af311f-cbc8-42c2-b801-7115619c3776"]},{"database_id":"4c658817-20db-4f56-b7f9-0637a22dfeb6","created_at":"0","views":["7d2148fc-cace-4452-9c5c-96e52e6bf8b5","e97877f5-c365-4025-9e6a-e590c4b19dbb","f0c59921-04ee-4971-995c-79b7fd8c00e2","7eb697cd-6a55-40bb-96ac-0d4a3bc924b2"]},{"database_id":"ee63da2b-aa2a-4d0b-aab0-59008635363a","created_at":"0","views":["2c1ee95a-1b09-4a1f-8d5e-501bc4861a9d","91ea7c08-f6b3-4b81-aa1e-d3664686186f"]},{"database_id":"e788f014-d0d3-4dfe-81ef-aa1ebb4d6366","created_at":"0","views":["1b0e322d-4909-4c63-914a-d034fc363097","350f425b-b671-4e2d-8182-5998a6e62924"]},{"database_id":"ad7dc45b-44b5-498f-bfa2-0f43bf05cc0d","created_at":"0","views":["0ce13415-6cce-4497-94c6-475ad96c249e","e4c89421-12b2-4d02-863d-20949eec9271"]},{"database_id":"ce267d12-3b61-4ebb-bb03-d65272f5f817","created_at":"0","views":["ee3ae8ce-959a-4df3-8734-40b535ff88e3","66a6f3bc-c78f-4f74-a09e-08d4717bf1fd","2bf50c03-f41f-4363-b5b1-101216a6c5cc"]}]
[
{
"database_id": "037a985f-f369-4c4a-8011-620012850a68",
"created_at": "1713429700",
"views": [
"48c52cf7-bf98-43fa-96ad-b31aade9b071"
]
},
{
"database_id": "daea6aee-9365-4703-a8e2-a2fa6a07b214",
"created_at": "1714449533",
"views": [
"b6347acb-3174-4f0e-98e9-dcce07e5dbf7"
]
},
{
"database_id": "4c658817-20db-4f56-b7f9-0637a22dfeb6",
"created_at": "0",
"views": [
"7d2148fc-cace-4452-9c5c-96e52e6bf8b5",
"e410747b-5f2f-45a0-b2f7-890ad3001355",
"2143e95d-5dcb-4e0f-bb2c-50944e6e019f",
"a5566e49-f156-4168-9b2d-17926c5da329",
"135615fa-66f7-4451-9b54-d7e99445fca4",
"b4e77203-5c8b-48df-bbc5-2e1143eb0e61",
"a6af311f-cbc8-42c2-b801-7115619c3776"
]
},
{
"database_id": "4c658817-20db-4f56-b7f9-0637a22dfeb6",
"created_at": "0",
"views": [
"7d2148fc-cace-4452-9c5c-96e52e6bf8b5",
"e97877f5-c365-4025-9e6a-e590c4b19dbb",
"f0c59921-04ee-4971-995c-79b7fd8c00e2",
"7eb697cd-6a55-40bb-96ac-0d4a3bc924b2"
]
},
{
"database_id": "ee63da2b-aa2a-4d0b-aab0-59008635363a",
"created_at": "0",
"views": [
"2c1ee95a-1b09-4a1f-8d5e-501bc4861a9d",
"91ea7c08-f6b3-4b81-aa1e-d3664686186f"
]
},
{
"database_id": "e788f014-d0d3-4dfe-81ef-aa1ebb4d6366",
"created_at": "0",
"views": [
"1b0e322d-4909-4c63-914a-d034fc363097",
"350f425b-b671-4e2d-8182-5998a6e62924"
]
},
{
"database_id": "ad7dc45b-44b5-498f-bfa2-0f43bf05cc0d",
"created_at": "0",
"views": [
"0ce13415-6cce-4497-94c6-475ad96c249e",
"e4c89421-12b2-4d02-863d-20949eec9271"
]
},
{
"database_id": "ce267d12-3b61-4ebb-bb03-d65272f5f817",
"created_at": "0",
"views": [
"ee3ae8ce-959a-4df3-8734-40b535ff88e3",
"66a6f3bc-c78f-4f74-a09e-08d4717bf1fd",
"2bf50c03-f41f-4363-b5b1-101216a6c5cc"
]
},
{
"database_id": "87bc006e-c1eb-47fd-9ac6-e39b17956369",
"created_at": "0",
"views": [
"7f233be4-1b4d-46b2-bcfc-f341b8d75267",
"a734a068-e73d-4b4b-853c-4daffea389c0"
]
}
]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -36,12 +36,36 @@ declare global {
mockCurrentWorkspace: () => void;
mockGetWorkspaceDatabases: () => void;
mockDocument: (id: string) => void;
clickOutside: () => void;
getTestingSelector: (testId: string) => Chainable<JQuery<HTMLElement>>;
}
}
}
Cypress.Commands.add('mount', mount);
Cypress.Commands.add('getTestingSelector', (testId: string) => {
return cy.get(`[data-testid="${testId}"]`);
});
Cypress.Commands.add('clickOutside', () => {
cy.document().then((doc) => {
// [0, 0] is the top left corner of the window
const x = 0;
const y = 0;
const evt = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window,
clientX: x,
clientY: y,
});
// Dispatch the event
doc.elementFromPoint(x, y)?.dispatchEvent(evt);
});
});
// Example use:
// cy.mount(<MyComponent />)

View File

@ -1,5 +1,5 @@
import { ViewLayout, YFolder, YjsFolderKey } from '@/application/collab.type';
import { createContext, useCallback, useContext } from 'react';
import { createContext, useContext } from 'react';
import { useParams } from 'react-router-dom';
export interface Crumb {
@ -36,14 +36,3 @@ export const useNavigateToView = () => {
export const useCrumbs = () => {
return useContext(FolderContext)?.crumbs;
};
export const usePushCrumb = () => {
const { setCrumbs } = useContext(FolderContext) || {};
return useCallback(
(crumb: Crumb) => {
setCrumbs?.((prevCrumbs) => [...prevCrumbs, crumb]);
},
[setCrumbs]
);
};

View File

@ -100,6 +100,7 @@ export async function batchCollabs(
const res = await batchFetchCollab(workspaceId, params);
console.log('Fetched collab data:', res);
for (const id of Object.keys(res)) {
const type = params.find((param) => param.object_id === id)?.collab_type;
const data = res[id];

View File

@ -1,123 +0,0 @@
import { YDoc, YFolder, YjsEditorKey } from '@/application/collab.type';
import { applyYDoc } from '@/application/ydoc/apply';
import { FolderProvider } from '@/components/_shared/context-provider/FolderProvider';
import { IdProvider } from '@/components/_shared/context-provider/IdProvider';
import withAppWrapper from '@/components/app/withAppWrapper';
import { useState } from 'react';
import { Database } from './Database';
import { DatabaseContextProvider } from './DatabaseContext';
import * as Y from 'yjs';
import '@/components/layout/layout.scss';
describe('<Database />', () => {
beforeEach(() => {
cy.viewport(1280, 720);
Object.defineProperty(window.navigator, 'language', { value: 'en-US' });
Object.defineProperty(window.navigator, 'languages', { value: ['en-US'] });
cy.mockDatabase();
});
it('renders with a database', () => {
cy.fixture('folder').then((folderJson) => {
const doc = new Y.Doc();
const state = new Uint8Array(folderJson.data.doc_state);
applyYDoc(doc, state);
const folder = doc.getMap(YjsEditorKey.data_section).get(YjsEditorKey.folder) as YFolder;
cy.fixture(`database/4c658817-20db-4f56-b7f9-0637a22dfeb6`).then((database) => {
cy.fixture(`database/rows/4c658817-20db-4f56-b7f9-0637a22dfeb6`).then((rows) => {
const doc = new Y.Doc();
const rootRowsDoc = new Y.Doc();
const rowsFolder: Y.Map<YDoc> = rootRowsDoc.getMap();
const databaseState = new Uint8Array(database.data.doc_state);
applyYDoc(doc, databaseState);
Object.keys(rows).forEach((key) => {
const data = rows[key];
const rowDoc = new Y.Doc();
applyYDoc(rowDoc, new Uint8Array(data));
rowsFolder.set(key, rowDoc);
});
const onNavigateToView = cy.stub();
const AppWrapper = withAppWrapper(() => {
return (
<div className={'flex h-screen w-screen flex-col py-4'}>
<TestDatabase
databaseDoc={doc}
rows={rowsFolder}
folder={folder}
iidIndex={'7d2148fc-cace-4452-9c5c-96e52e6bf8b5'}
initialViewId={'7d2148fc-cace-4452-9c5c-96e52e6bf8b5'}
onNavigateToView={onNavigateToView}
/>
</div>
);
});
cy.mount(<AppWrapper />);
cy.get('[data-testid^=view-tab-]').should('have.length', 4);
cy.get('.database-grid').should('exist');
cy.get('[data-testid=view-tab-e410747b-5f2f-45a0-b2f7-890ad3001355]').click();
cy.get('.database-board').should('exist');
cy.wrap(onNavigateToView).should('have.been.calledOnceWith', 'e410747b-5f2f-45a0-b2f7-890ad3001355');
cy.wait(800);
cy.get('[data-testid=view-tab-7d2148fc-cace-4452-9c5c-96e52e6bf8b5]').click();
cy.get('.database-grid').should('exist');
cy.wrap(onNavigateToView).should('have.been.calledWith', '7d2148fc-cace-4452-9c5c-96e52e6bf8b5');
cy.wait(800);
cy.get('[data-testid=view-tab-2143e95d-5dcb-4e0f-bb2c-50944e6e019f]').click();
cy.get('.database-calendar').should('exist');
cy.wrap(onNavigateToView).should('have.been.calledWith', '2143e95d-5dcb-4e0f-bb2c-50944e6e019f');
});
});
});
});
});
function TestDatabase({
databaseDoc,
rows,
folder,
iidIndex,
initialViewId,
onNavigateToView,
}: {
databaseDoc: YDoc;
rows: Y.Map<YDoc>;
folder: YFolder;
iidIndex: string;
initialViewId: string;
onNavigateToView: (viewId: string) => void;
}) {
const [activeViewId, setActiveViewId] = useState<string>(initialViewId);
const handleNavigateToView = (viewId: string) => {
setActiveViewId(viewId);
onNavigateToView(viewId);
};
return (
<FolderProvider folder={folder}>
<IdProvider objectId={iidIndex}>
<DatabaseContextProvider
viewId={activeViewId || iidIndex}
databaseDoc={databaseDoc}
rowDocMap={rows}
readOnly={true}
>
<Database iidIndex={iidIndex} viewId={activeViewId} onNavigateToView={handleNavigateToView} />
</DatabaseContextProvider>
</IdProvider>
</FolderProvider>
);
}

View File

@ -0,0 +1,40 @@
import { renderDatabase } from '@/components/database/__tests__/withTestingDatabase';
import '@/components/layout/layout.scss';
describe('<Database />', () => {
beforeEach(() => {
cy.viewport(1280, 720);
Object.defineProperty(window.navigator, 'language', { value: 'en-US' });
cy.mockDatabase();
});
it('renders with a database', () => {
const onNavigateToView = cy.stub();
renderDatabase(
{
databaseId: '4c658817-20db-4f56-b7f9-0637a22dfeb6',
viewId: '7d2148fc-cace-4452-9c5c-96e52e6bf8b5',
onNavigateToView,
},
() => {
cy.get('[data-testid^=view-tab-]').should('have.length', 4);
cy.get('.database-grid').should('exist');
cy.get('[data-testid=view-tab-e410747b-5f2f-45a0-b2f7-890ad3001355]').click();
cy.get('.database-board').should('exist');
cy.wrap(onNavigateToView).should('have.been.calledOnceWith', 'e410747b-5f2f-45a0-b2f7-890ad3001355');
cy.wait(800);
cy.get('[data-testid=view-tab-7d2148fc-cace-4452-9c5c-96e52e6bf8b5]').click();
cy.get('.database-grid').should('exist');
cy.wrap(onNavigateToView).should('have.been.calledWith', '7d2148fc-cace-4452-9c5c-96e52e6bf8b5');
cy.wait(800);
cy.get('[data-testid=view-tab-2143e95d-5dcb-4e0f-bb2c-50944e6e019f]').click();
cy.get('.database-calendar').should('exist');
cy.wrap(onNavigateToView).should('have.been.calledWith', '2143e95d-5dcb-4e0f-bb2c-50944e6e019f');
}
);
});
});

View File

@ -3,8 +3,8 @@ import { applyYDoc } from '@/application/ydoc/apply';
import { FolderProvider } from '@/components/_shared/context-provider/FolderProvider';
import { IdProvider } from '@/components/_shared/context-provider/IdProvider';
import withAppWrapper from '@/components/app/withAppWrapper';
import { DatabaseRow } from './DatabaseRow';
import { DatabaseContextProvider } from './DatabaseContext';
import { DatabaseRow } from 'src/components/database/DatabaseRow';
import { DatabaseContextProvider } from 'src/components/database/DatabaseContext';
import * as Y from 'yjs';
import '@/components/layout/layout.scss';

View File

@ -0,0 +1,99 @@
import { renderDatabase } from '@/components/database/__tests__/withTestingDatabase';
import '@/components/layout/layout.scss';
describe('<Database /> with filters and sorts', () => {
beforeEach(() => {
cy.viewport(1280, 720);
Object.defineProperty(window.navigator, 'language', { value: 'en-US' });
cy.mockDatabase();
});
it('render a database with filters and sorts', () => {
const onNavigateToView = cy.stub();
renderDatabase(
{
onNavigateToView,
databaseId: '87bc006e-c1eb-47fd-9ac6-e39b17956369',
viewId: '7f233be4-1b4d-46b2-bcfc-f341b8d75267',
},
() => {
cy.wait(1000);
cy.getTestingSelector('database-actions-filter').click();
cy.get('.database-conditions').then(($el) => {
cy.wait(500);
const height = $el.height();
expect(height).to.be.greaterThan(0);
});
cy.getTestingSelector('database-sort-condition').click();
cy.wait(500);
cy.getTestingSelector('sort-condition').as('sortConditions').should('have.length', 2);
cy.get('@sortConditions').eq(0).contains('number');
cy.get('@sortConditions').eq(0).contains('Ascending');
cy.get('@sortConditions').eq(1).contains('Name');
cy.get('@sortConditions').eq(1).contains('Descending');
cy.clickOutside();
cy.getTestingSelector('sort-condition-list').should('not.exist');
// the length of filters should be 6
cy.getTestingSelector('database-filter-condition').as('filterConditions');
cy.get('@filterConditions').should('have.length', 6);
// the first filter should be 'Name', the value should be 'contains', and the input should be 123
cy.get('@filterConditions').eq(0).as('filterCondition');
cy.get('@filterCondition').contains('Name');
cy.get('@filterCondition').contains('123');
cy.get('@filterCondition').click();
cy.getTestingSelector('filter-menu-popover').should('be.visible');
cy.getTestingSelector('filter-condition-type').contains('Contains');
cy.get(`[data-testid="text-filter-input"] input`).should('have.value', '123');
cy.clickOutside();
// the second filter should be 'Type', the value should be 'is not empty'
cy.get('@filterConditions').eq(1).as('filterCondition');
cy.get('@filterCondition').contains('Type');
cy.get('@filterCondition').contains('is not empty');
cy.get('@filterCondition').click();
cy.clickOutside();
// the third filter should be 'Done', the value should be 'is Checked'
cy.get('@filterConditions').eq(2).as('filterCondition');
cy.get('@filterCondition').contains('Done');
cy.get('@filterCondition').contains('is Checked');
cy.get('@filterCondition').click();
cy.clickOutside();
// the fourth filter should be 'Number', the value should be 'is greater than', and the input should be 600
cy.get('@filterConditions').eq(3).as('filterCondition');
cy.get('@filterCondition').contains('number');
cy.get('@filterCondition').contains('> 600');
cy.get('@filterCondition').click();
cy.getTestingSelector('filter-menu-popover').should('be.visible');
cy.getTestingSelector('filter-condition-type').contains('Is greater than');
cy.get(`[data-testid="number-filter-input"] input`).should('have.value', '600');
cy.clickOutside();
// the fifth filter should be 'multi type', the value should be 'Does not contain'
cy.get('@filterConditions').eq(4).as('filterCondition');
cy.get('@filterCondition').contains('multi type');
cy.get('@filterCondition').click();
cy.getTestingSelector('filter-menu-popover').should('be.visible');
cy.getTestingSelector('filter-condition-type').contains('Does not contain');
cy.getTestingSelector('select-option-list').as('selectOptionList');
cy.get('@selectOptionList').should('have.length', 2);
cy.get('@selectOptionList').eq(0).contains('option-2');
cy.get('@selectOptionList').eq(1).contains('option-1');
cy.get('@selectOptionList').eq(1).should('have.data', 'checked', true);
cy.clickOutside();
// the sixth filter should be 'Checklist', the value should be 'is completed'
cy.get('@filterConditions').eq(5).as('filterCondition');
cy.get('@filterCondition').contains('Checklist');
cy.get('@filterCondition').contains('is complete');
cy.get('@filterCondition').click();
cy.clickOutside();
cy.getTestingSelector('view-tab-a734a068-e73d-4b4b-853c-4daffea389c0').click();
cy.wait(800);
cy.getTestingSelector('view-tab-7f233be4-1b4d-46b2-bcfc-f341b8d75267').click();
}
);
});
});

View File

@ -0,0 +1,106 @@
import { YDoc, YFolder, YjsEditorKey } from '@/application/collab.type';
import { applyYDoc } from '@/application/ydoc/apply';
import { FolderProvider } from '@/components/_shared/context-provider/FolderProvider';
import { IdProvider } from '@/components/_shared/context-provider/IdProvider';
import withAppWrapper from '@/components/app/withAppWrapper';
import { DatabaseContextProvider } from '@/components/database/DatabaseContext';
import { useState } from 'react';
import * as Y from 'yjs';
import { Database } from 'src/components/database/Database';
export function renderDatabase(
{
databaseId,
viewId,
onNavigateToView,
}: {
databaseId: string;
viewId: string;
onNavigateToView: (viewId: string) => void;
},
onAfterRender?: () => void
) {
cy.fixture('folder').then((folderJson) => {
const doc = new Y.Doc();
const state = new Uint8Array(folderJson.data.doc_state);
applyYDoc(doc, state);
const folder = doc.getMap(YjsEditorKey.data_section).get(YjsEditorKey.folder) as YFolder;
cy.fixture(`database/${databaseId}`).then((database) => {
cy.fixture(`database/rows/${databaseId}`).then((rows) => {
const doc = new Y.Doc();
const rootRowsDoc = new Y.Doc();
const rowsFolder: Y.Map<YDoc> = rootRowsDoc.getMap();
const databaseState = new Uint8Array(database.data.doc_state);
applyYDoc(doc, databaseState);
Object.keys(rows).forEach((key) => {
const data = rows[key];
const rowDoc = new Y.Doc();
applyYDoc(rowDoc, new Uint8Array(data));
rowsFolder.set(key, rowDoc);
});
const AppWrapper = withAppWrapper(() => {
return (
<div className={'flex h-screen w-screen flex-col py-4'}>
<TestDatabase
databaseDoc={doc}
rows={rowsFolder}
folder={folder}
iidIndex={viewId}
initialViewId={viewId}
onNavigateToView={onNavigateToView}
/>
</div>
);
});
cy.mount(<AppWrapper />);
onAfterRender?.();
});
});
});
}
export function TestDatabase({
databaseDoc,
rows,
folder,
iidIndex,
initialViewId,
onNavigateToView,
}: {
databaseDoc: YDoc;
rows: Y.Map<YDoc>;
folder: YFolder;
iidIndex: string;
initialViewId: string;
onNavigateToView: (viewId: string) => void;
}) {
const [activeViewId, setActiveViewId] = useState<string>(initialViewId);
const handleNavigateToView = (viewId: string) => {
setActiveViewId(viewId);
onNavigateToView(viewId);
};
return (
<FolderProvider folder={folder}>
<IdProvider objectId={iidIndex}>
<DatabaseContextProvider
viewId={activeViewId || iidIndex}
databaseDoc={databaseDoc}
rowDocMap={rows}
readOnly={true}
>
<Database iidIndex={iidIndex} viewId={activeViewId} onNavigateToView={handleNavigateToView} />
</DatabaseContextProvider>
</IdProvider>
</FolderProvider>
);
}

View File

@ -17,11 +17,13 @@ export function DatabaseActions() {
onClick={() => {
conditionsContext?.toggleExpanded();
}}
data-testid={'database-actions-filter'}
color={filter.length > 0 ? 'primary' : 'inherit'}
>
{t('grid.settings.filter')}
</TextButton>
<TextButton
data-testid={'database-actions-sort'}
onClick={() => {
conditionsContext?.toggleExpanded();
}}

View File

@ -16,8 +16,13 @@ export function SelectOptionList({ fieldId, selectedIds }: { fieldId: string; se
const isSelected = selectedIds.includes(option.id);
return (
<div key={option.id} className={'flex items-center justify-between gap-2'}>
<Tag label={option.name} color={SelectOptionColorMap[option.color]} />
<div
key={option.id}
data-testid={'select-option-list'}
data-checked={isSelected}
className={'flex items-center justify-between gap-2 text-xs'}
>
<Tag size={'small'} label={option.name} color={SelectOptionColorMap[option.color]} />
{isSelected && <CheckIcon />}
</div>
);

View File

@ -19,6 +19,7 @@ function Filter({ filterId }: { filterId: string }) {
onClick={(e) => {
setAnchorEl(e.currentTarget);
}}
data-testid={'database-filter-condition'}
className={
'flex cursor-pointer flex-nowrap items-center gap-1 rounded-full border border-line-divider py-1 px-2 hover:border-fill-default hover:text-fill-default hover:shadow-sm'
}
@ -39,6 +40,7 @@ function Filter({ filterId }: { filterId: string }) {
onClose={() => {
setAnchorEl(null);
}}
data-testid={'filter-menu-popover'}
slotProps={{
paper: {
style: {

View File

@ -10,7 +10,10 @@ function FieldMenuTitle({ fieldId, selectedConditionText }: { fieldId: string; s
</div>
<div className={'flex flex-1 items-center justify-end'}>
<div className={'flex items-center gap-1'}>
<div className={'overflow max-w-[100px] truncate whitespace-nowrap text-xs font-normal'}>
<div
data-testid={'filter-condition-type'}
className={'overflow max-w-[100px] truncate whitespace-nowrap text-xs font-normal'}
>
{selectedConditionText}
</div>
<ArrowDownSvg />

View File

@ -58,6 +58,7 @@ function NumberFilterMenu({ filter }: { filter: NumberFilter }) {
{displayTextField && (
<TextField
disabled={readOnly}
data-testid={'number-filter-input'}
spellCheck={false}
inputProps={{
className: 'text-xs p-1.5',

View File

@ -53,10 +53,11 @@ function TextFilterMenu({ filter }: { filter: TextFilter }) {
}, [filter.condition]);
return (
<div className={'flex flex-col gap-2 p-2'}>
<div className={'flex flex-col gap-2 p-2'} data-testid='text-filter'>
<FieldMenuTitle fieldId={filter.fieldId} selectedConditionText={selectedCondition?.text ?? ''} />
{displayTextField && (
<TextField
data-testid='text-filter-input'
disabled={readOnly}
spellCheck={false}
inputProps={{

View File

@ -8,7 +8,7 @@ function Sort({ sortId }: { sortId: string }) {
if (!sort) return null;
return (
<div className={'flex items-center gap-1.5'}>
<div data-testid={'sort-condition'} className={'flex items-center gap-1.5'}>
<div className={'w-[120px] max-w-[250px] overflow-hidden rounded-full border border-line-divider py-1 px-2 '}>
<FieldDisplay fieldId={sort.fieldId} />
</div>

View File

@ -19,6 +19,7 @@ export function Sorts() {
onClick={(e) => {
setAnchorEl(e.currentTarget);
}}
data-testid={'database-sort-condition'}
className='flex cursor-pointer items-center gap-1 rounded-full border border-line-divider px-2 py-1 text-xs hover:border-fill-default hover:text-fill-default hover:shadow-sm'
>
<SortSvg />

View File

@ -73,7 +73,7 @@ export function useLayout() {
return {
viewId: view.get(YjsFolderKey.id),
name: view.get(YjsFolderKey.name),
icon: icon || '',
icon: icon || view.get(YjsFolderKey.layout),
};
})
.slice(1)

View File

@ -1,6 +1,31 @@
import { ViewLayout } from '@/application/collab.type';
import { Crumb, useNavigateToView } from '@/application/folder-yjs';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { ReactComponent as DocumentSvg } from '$icons/16x/document.svg';
import { ReactComponent as GridSvg } from '$icons/16x/grid.svg';
import { ReactComponent as BoardSvg } from '$icons/16x/board.svg';
import { ReactComponent as CalendarSvg } from '$icons/16x/date.svg';
const renderCrumbIcon = (icon: string) => {
if (Number(icon) === ViewLayout.Grid) {
return <GridSvg />;
}
if (Number(icon) === ViewLayout.Board) {
return <BoardSvg />;
}
if (Number(icon) === ViewLayout.Calendar) {
return <CalendarSvg />;
}
if (Number(icon) === ViewLayout.Document) {
return <DocumentSvg />;
}
return icon;
};
function Item({ crumb, disableClick = false }: { crumb: Crumb; disableClick?: boolean }) {
const { viewId, icon, name } = crumb;
@ -16,7 +41,7 @@ function Item({ crumb, disableClick = false }: { crumb: Crumb; disableClick?: bo
onNavigateToView?.(viewId);
}}
>
{icon}
<span className={'h-4 w-4'}>{renderCrumbIcon(icon)}</span>
<span
className={!disableClick ? 'max-w-[250px] truncate hover:text-fill-default hover:underline' : 'flex-1 truncate'}
>

View File

@ -78,7 +78,7 @@ export default defineConfig({
port: !!process.env.TAURI_PLATFORM ? 5173 : process.env.PORT ? parseInt(process.env.PORT) : 3000,
strictPort: true,
watch: {
ignored: ['**/__tests__/**', '**/cypress/**', 'node_modules', '**/*.cy.tsx', '**/*.cy.ts', 'cypress'],
ignored: ['node_modules'],
},
cors: false,
},
@ -143,6 +143,10 @@ export default defineConfig({
'@mui/icons-material/ErrorOutline',
'@mui/icons-material/CheckCircleOutline',
'@mui/icons-material/FunctionsOutlined',
'react-katex',
// 'react-custom-scrollbars-2',
// 'react-window',
// 'react-virtualized-auto-sizer',
],
},
});