Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,26 +1,44 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { act, screen } from '@testing-library/react';

import ElementCounter, { getCounterContent } from './ElementCounter';
import { setup } from '../../../../utils/tests/setup';
import { DatatableStore, datatableInitialState } from '../../Datatable.store';

// Mock translation function for tests
const mockT = (key: string, options?: any) => {
// Remove the 'sscds|' prefix if present
const cleanKey = key.replace('sscds|', '');

const translations: Record<string, string> = {
'datatable.elementCounter.selectedOf': `${
options?.selectedLength || ''
} of ${options?.totalLength || ''} selected`,
'datatable.elementCounter.noData': 'No data',
'datatable.selection.selectAll': 'Select All',
'datatable.selection.selectNone': 'Select None',
};

return translations[cleanKey] || key;
};

describe('getCounterContent', () => {
it('should return No Data when "totalLength" is 0', () => {
setup(getCounterContent(0));
setup(getCounterContent(0, mockT as any, 0));
expect(screen.getByTestId('counter-content')).toHaveTextContent('No data');
});
it('should return No Data when "totalLength" is 0 and "selectedLength" is greater than 0', () => {
setup(getCounterContent(0, 500));
setup(getCounterContent(0, mockT as any, 500));
expect(screen.getByTestId('counter-content')).toHaveTextContent('No data');
});

it('should return correct count when "totalLength" is greater than 0', () => {
setup(getCounterContent(1000));
setup(getCounterContent(1000, mockT as any, 0));
expect(screen.getByTestId('counter-content')).toHaveTextContent('1K');
});

it('should return correct count when "totalLength" and "selectedLength" are greater than 0', () => {
setup(getCounterContent(1000, 500));
setup(getCounterContent(1000, mockT as any, 500));
expect(screen.getByTestId('counter-content')).toHaveTextContent(
'500 of 1K selected',
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import { isPositive } from 'ramda-adjunct';
import styled from 'styled-components';
import { TFunction } from 'i18next';

import { Text } from '../../../Text';
import { abbreviateNumber, getColor, getSpace } from '../../../../utils';
Expand All @@ -11,6 +12,7 @@ import { DatatableStore } from '../../Datatable.store';
import { ElementCounterProps } from './ElementCounter.types';
import { Inline } from '../../../layout';
import { SpaceSizes } from '../../../../theme';
import { useSafeTranslation } from '../../../../hooks/useSafeTranslation';

const SelectionButton = styled.button`
display: inline-flex;
Expand All @@ -29,15 +31,20 @@ const SelectionButton = styled.button`
}
`;

export const getCounterContent = (totalLength: number, selectedLength = 0) => (
export const getCounterContent = (
totalLength: number,
t: TFunction<['sscds'], undefined>,
selectedLength = 0,
) => (
<span aria-hidden="true" data-testid="counter-content">
{isPositive(selectedLength) && isPositive(totalLength)
? `${abbreviateNumber(selectedLength)} of ${abbreviateNumber(
totalLength,
)} selected`
? t('sscds|datatable.elementCounter.selectedOf', {
selectedLength: abbreviateNumber(selectedLength),
totalLength: abbreviateNumber(totalLength),
})
: isPositive(totalLength)
? abbreviateNumber(totalLength)
: 'No data'}
: t('sscds|datatable.elementCounter.noData')}
</span>
);

Expand All @@ -55,6 +62,7 @@ const ElementCounter = ({
hasSelection,
hasOnlyPerPageSelection,
}: ElementCounterProps) => {
const { t } = useSafeTranslation();
const { selectedIds, hasExclusiveSelection } = DatatableStore.useState(
(s) => ({
selectedIds: s.selectedIds,
Expand All @@ -74,7 +82,7 @@ const ElementCounter = ({
? dataSize - localSelectedLength
: localSelectedLength;

const content = getCounterContent(dataSize, computedSelectedLength);
const content = getCounterContent(dataSize, t, computedSelectedLength);

const handleSelectAllClick = () => {
setLocalSelectedLength(0);
Expand Down Expand Up @@ -111,7 +119,7 @@ const ElementCounter = ({
{localSelectedLength > 0 && (
<SelectionButton
aria-label="Select None"
title="Select None"
title={t('sscds|datatable.selection.selectNone')}
type="button"
onClick={handleSelectNoneClick}
>
Expand All @@ -132,12 +140,12 @@ const ElementCounter = ({
actions={[
{
name: 'select-all',
label: 'Select All',
label: t('sscds|datatable.selection.selectAll'),
onClick: handleSelectAllClick,
},
{
name: 'select-none',
label: 'Select None',
label: t('sscds|datatable.selection.selectNone'),
onClick: handleSelectNoneClick,
},
]}
Expand Down
56 changes: 30 additions & 26 deletions src/components/Datatable/Table/components/LoadingOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { LoadingOverlayProps } from './LoadingOverlay.types';
import { Spinner } from '../../../Spinner';
import Button from '../../../ButtonV2/Button';
import { Inline, Padbox } from '../../../layout';
import { useSafeTranslation } from '../../../../hooks/useSafeTranslation';

const overlayStyles = css`
position: absolute;
Expand Down Expand Up @@ -46,31 +47,34 @@ const LoadingBackground = styled.div`
opacity: 0.75;
`;

const LoadingOverlay = ({ isCancelable, onCancel }: LoadingOverlayProps) => (
<LoadingOverlayContainer>
<LoadingBackground />
<LoadingIndicatorContainer>
<Padbox paddingSize="sm">
{isCancelable ? (
<Inline align="center" gap="sm">
<Spinner
height={24}
horizontalMargin={0}
verticalMargin={0}
width={24}
dark
/>
<LoadingText>Loading</LoadingText>
<Button variant="subtle" onClick={onCancel}>
Cancel
</Button>
</Inline>
) : (
<Spinner height={48} verticalMargin={0} width={48} dark />
)}
</Padbox>
</LoadingIndicatorContainer>
</LoadingOverlayContainer>
);
const LoadingOverlay = ({ isCancelable, onCancel }: LoadingOverlayProps) => {
const { t } = useSafeTranslation();
return (
<LoadingOverlayContainer>
<LoadingBackground />
<LoadingIndicatorContainer>
<Padbox paddingSize="sm">
{isCancelable ? (
<Inline align="center" gap="sm">
<Spinner
height={24}
horizontalMargin={0}
verticalMargin={0}
width={24}
dark
/>
<LoadingText>{t('sscds|loading')}</LoadingText>
<Button variant="subtle" onClick={onCancel}>
{t('sscds|cancel')}
</Button>
</Inline>
) : (
<Spinner height={48} verticalMargin={0} width={48} dark />
)}
</Padbox>
</LoadingIndicatorContainer>
</LoadingOverlayContainer>
);
};

export default LoadingOverlay;
39 changes: 20 additions & 19 deletions src/components/Datatable/Table/components/NoMatchingData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { pxToRem } from '../../../../utils';
import { H4 } from '../../../Heading';
import Paragraph from '../../../Paragraph/Paragraph';
import { TextSizes } from '../../../Text/Text.enums';
import { useSafeTranslation } from '../../../../hooks/useSafeTranslation';

const ListItem = styled.li`
margin-left: ${pxToRem(16)};
Expand All @@ -13,24 +14,24 @@ const ListItem = styled.li`
}
`;

const NoMatchingData = () => (
<>
<H4 margin={{ top: 0, bottom: 0.8 }}>
No items match your current filters
</H4>
<Paragraph margin={{ bottom: 0.8 }} size={TextSizes.md}>
You could try
</Paragraph>
<Paragraph as="div" margin="none" size={TextSizes.md}>
<ul>
<ListItem>
using &quot;OR&quot; instead of &quot;AND&quot; to combine your
queries
</ListItem>
<ListItem>checking your queries for typos</ListItem>
</ul>
</Paragraph>
</>
);
const NoMatchingData = () => {
const { t } = useSafeTranslation();
return (
<>
<H4 margin={{ top: 0, bottom: 0.8 }}>
{t('sscds|datatable.noMatchingData.title')}
</H4>
<Paragraph margin={{ bottom: 0.8 }} size={TextSizes.md}>
{t('sscds|datatable.noMatchingData.description')}
</Paragraph>
<Paragraph as="div" margin="none" size={TextSizes.md}>
<ul>
<ListItem>{t('sscds|datatable.noMatchingData.list.item1')}</ListItem>
<ListItem>{t('sscds|datatable.noMatchingData.list.item2')}</ListItem>
</ul>
</Paragraph>
</>
);
};

export default NoMatchingData;
4 changes: 3 additions & 1 deletion src/components/_internal/BaseTable/Footer/GoToPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Input } from '../../../forms';
import { GoToPageProps } from './GoToPage.types';
import { SpaceSizes } from '../../../../theme';
import { Text } from '../../../Text';
import { useSafeTranslation } from '../../../../hooks/useSafeTranslation';

const SmallInput = styled(Input)`
padding: ${pxToRem(8, 16)};
Expand All @@ -26,6 +27,7 @@ const SmallInput = styled(Input)`
`;

const GoToPage = ({ pageCount, onPageChange }: GoToPageProps) => {
const { t } = useSafeTranslation();
const inputRef = useRef<HTMLInputElement>(null);
const handlePageChange = (e: KeyboardEvent<HTMLInputElement>) => {
const { target, key } = e;
Expand All @@ -48,7 +50,7 @@ const GoToPage = ({ pageCount, onPageChange }: GoToPageProps) => {
return (
<Inline align="center" gap={SpaceSizes.md}>
<Text as="label" htmlFor="goToPageInput" variant="subtle">
Go to page:
{t('sscds|basetable.goToPage.label')}
</Text>
<SmallInput
ref={inputRef}
Expand Down
40 changes: 22 additions & 18 deletions src/components/_internal/BaseTable/components/LoadingNoData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,28 @@ import { TextSizes } from '../../../Text/Text.enums';
import { SpaceSizes } from '../../../../theme';
import { H4 } from '../../../Heading';
import Paragraph from '../../../Paragraph/Paragraph';
import { useSafeTranslation } from '../../../../hooks/useSafeTranslation';

const LoadingNoData = () => (
<>
<Inline align="center" gap={SpaceSizes.sm}>
<Spinner
borderWidth={2}
height={16}
horizontalMargin={0}
verticalMargin={0}
width={16}
dark
/>
<H4 margin={{ top: 0, bottom: 0, left: 0.5 }}>Loading</H4>
</Inline>
<Paragraph margin={{ top: 0.8, bottom: 0.8 }} size={TextSizes.md}>
This may take a few moments.
</Paragraph>
</>
);
const LoadingNoData = () => {
const { t } = useSafeTranslation();
return (
<>
<Inline align="center" gap={SpaceSizes.sm}>
<Spinner
borderWidth={2}
height={16}
horizontalMargin={0}
verticalMargin={0}
width={16}
dark
/>
<H4 margin={{ top: 0, bottom: 0, left: 0.5 }}>{t('sscds|loading')}</H4>
</Inline>
<Paragraph margin={{ top: 0.8, bottom: 0.8 }} size={TextSizes.md}>
{t('sscds|basetable.loading.description')}
</Paragraph>
</>
);
};

export default LoadingNoData;
22 changes: 14 additions & 8 deletions src/components/_internal/BaseTable/components/NoData.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { useSafeTranslation } from '../../../../hooks/useSafeTranslation';
import { H4 } from '../../../Heading';
import Paragraph from '../../../Paragraph/Paragraph';
import { TextSizes } from '../../../Text/Text.enums';

const NoData = () => (
<>
<H4 margin={{ top: 0, bottom: 0.8 }}>No data available</H4>
<Paragraph margin={{ bottom: 0.8 }} size={TextSizes.md}>
There are no records in this database.
</Paragraph>
</>
);
const NoData = () => {
const { t } = useSafeTranslation();
return (
<>
<H4 margin={{ top: 0, bottom: 0.8 }}>
{t('sscds|basetable.noData.title')}
</H4>
<Paragraph margin={{ bottom: 0.8 }} size={TextSizes.md}>
{t('sscds|datatable.noRecords.title')}
</Paragraph>
</>
);
};

export default NoData;
20 changes: 20 additions & 0 deletions src/locales/cs-CZ.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ const locale = {
'datatable.selection.clearSelection': 'Zrušit výběr',
'datatable.selection.toggleAll': 'Přepnout výběr všech řádů',
'datatable.selection.toggleRow': 'Přepnout výběr řádku',
'datatable.selection.selectAll': 'Vybrat vše',
'datatable.selection.selectNone': 'Nevybrat',
'datatable.elementCounter.selectedOf':
'{selectedLength} z {totalLength} vybráno',
'datatable.elementCounter.noData': 'Žádná data',
'datatable.noMatchingData.title':
'Žádné položky neodpovídají vašim aktuálním filtrům',
'datatable.noMatchingData.description': 'Můžete zkusit',
'datatable.noMatchingData.list.item1':
'použít "OR" místo "AND" pro kombinování vašich dotazů',
'datatable.noMatchingData.list.item2':
'zkontrolovat překlepy ve vašich dotazech',
'datatable.noRecords.title': 'V této databázi nejsou žádné záznamy.',
'datatable.noData.title': 'Nenalezeny žádné výsledky',
'datatable.noData.list.description': 'Toto může nastat z důvodu, že:',
'datatable.noData.list.emptySet': 'datová sada je prázdná',
Expand Down Expand Up @@ -84,5 +97,12 @@ const locale = {
'Přetahování bylo zrušeno. Sloupec {header} byl uvolněn.',
'datatable.settings.ordering.screenReader.notDroppableArea':
'Sloupec {activeHeader} již není nad oblastí, kam lze položku upustit.',
'basetable.noData.title': 'Žádná data nejsou k dispozici',
'basetable.noData.description':
'Nejsou k dispozici žádné záznamy k zobrazení.',
'basetable.loading.title': 'Načítám data',
'basetable.loading.description': 'Může to chvíli trvat.',
'basetable.goToPage.label': 'Jít na stránku:',
cancel: 'Zrušit',
} as const;
export default locale;
Loading
Loading