Skip to content
Closed
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
9 changes: 7 additions & 2 deletions src/generic/block-type-utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import {
BackHand as BackHandIcon,
BookOpen as BookOpenIcon,
Casino as ProblemBankIcon,
ContentPaste as ContentPasteIcon,
Edit as EditIcon,
EditNote as EditNoteIcon,
FormatListBulleted as FormatListBulletedIcon,
CalendarViewDay,
HelpOutline as HelpOutlineIcon,
LibraryAdd as LibraryIcon,
Lock as LockIcon,
Expand Down Expand Up @@ -35,7 +36,7 @@ export const COMPONENT_TYPES = {
export const UNIT_TYPE_ICONS_MAP: Record<string, React.ComponentType> = {
video: VideoCameraIcon,
other: BookOpenIcon,
vertical: FormatListBulletedIcon,
vertical: CalendarViewDay,
problem: EditIcon,
lock: LockIcon,
};
Expand All @@ -55,9 +56,12 @@ export const COMPONENT_TYPE_ICON_MAP: Record<string, React.ComponentType> = {

export const STRUCTURAL_TYPE_ICONS: Record<string, React.ComponentType> = {
vertical: UNIT_TYPE_ICONS_MAP.vertical,
unit: UNIT_TYPE_ICONS_MAP.vertical,
sequential: Folder,
chapter: Folder,
collection: Folder,
libraryContent: Folder,
paste: ContentPasteIcon,
};

export const COMPONENT_TYPE_STYLE_COLOR_MAP = {
Expand All @@ -70,6 +74,7 @@ export const COMPONENT_TYPE_STYLE_COLOR_MAP = {
[COMPONENT_TYPES.video]: 'component-style-video',
[COMPONENT_TYPES.dragAndDrop]: 'component-style-default',
vertical: 'component-style-vertical',
unit: 'component-style-vertical',
sequential: 'component-style-default',
chapter: 'component-style-default',
collection: 'component-style-collection',
Expand Down
14 changes: 14 additions & 0 deletions src/generic/component-count/ComponentCount.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { render, screen } from '@testing-library/react';
import ComponentCount from '.';

describe('<ComponentCount>', () => {
it('should render the component', () => {
render(<ComponentCount count={17} />);
expect(screen.getByText('17')).toBeInTheDocument();
});

it('should render the component with zero', () => {
render(<ComponentCount count={0} />);
expect(screen.getByText('0')).toBeInTheDocument();
});
});
18 changes: 18 additions & 0 deletions src/generic/component-count/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { Icon, Stack } from '@openedx/paragon';
import { Widgets } from '@openedx/paragon/icons';

type ComponentCountProps = {
count?: number;
};

const ComponentCount: React.FC<ComponentCountProps> = ({ count }) => (
count !== undefined ? (
<Stack direction="horizontal" gap={1}>
<Icon size="sm" src={Widgets} />
<small>{count}</small>
</Stack>
) : null
);

export default ComponentCount;
2 changes: 2 additions & 0 deletions src/generic/key-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ describe('component utils', () => {
['lb:Axim:beta:problem:571fe018-f3ce-45c9-8f53-5dafcb422fdd', 'lib:Axim:beta'],
['lib-collection:org:lib:coll', 'lib:org:lib'],
['lib-collection:OpenCraftX:ALPHA:coll', 'lib:OpenCraftX:ALPHA'],
['lct:org:lib:unit:my-unit-9284e2', 'lib:org:lib'],
['lct:OpenCraftX:ALPHA:my-unit-a3223f', 'lib:OpenCraftX:ALPHA'],
]) {
it(`returns '${expected}' for usage key '${input}'`, () => {
expect(getLibraryId(input)).toStrictEqual(expected);
Expand Down
10 changes: 4 additions & 6 deletions src/generic/key-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@ export function getBlockType(usageKey: string): string {
* @returns The library key, e.g. `lib:org:lib`
*/
export function getLibraryId(usageKey: string): string {
if (usageKey && (usageKey.startsWith('lb:') || usageKey.startsWith('lib-collection:'))) {
const org = usageKey.split(':')[1];
const lib = usageKey.split(':')[2];
if (org && lib) {
return `lib:${org}:${lib}`;
}
const [blockType, org, lib] = usageKey?.split(':') || [];

if (['lb', 'lib-collection', 'lct'].includes(blockType) && org && lib) {
return `lib:${org}:${lib}`;
}
throw new Error(`Invalid usageKey: ${usageKey}`);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import {
act,
fireEvent,
Expand All @@ -9,7 +8,7 @@ import LoadingButton from '.';

const buttonTitle = 'Button Title';

const RootWrapper = (onClick) => (
const RootWrapper = (onClick?: () => (Promise<void> | void)) => (
<LoadingButton label={buttonTitle} onClick={onClick} />
);

Expand All @@ -31,8 +30,8 @@ describe('<LoadingButton />', () => {
});

it('renders the spinner correctly', async () => {
let resolver;
const longFunction = () => new Promise((resolve) => {
let resolver: () => void;
const longFunction = () => new Promise<void>((resolve) => {
resolver = resolve;
});
const { container, getByRole, getByText } = render(RootWrapper(longFunction));
Expand All @@ -51,8 +50,8 @@ describe('<LoadingButton />', () => {
});

it('renders the spinner correctly even with error', async () => {
let rejecter;
const longFunction = () => new Promise((_resolve, reject) => {
let rejecter: (err: Error) => void;
const longFunction = () => new Promise<void>((_resolve, reject) => {
rejecter = reject;
});
const { container, getByRole, getByText } = render(RootWrapper(longFunction));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-check
import React, {
useCallback,
useEffect,
Expand All @@ -8,20 +7,20 @@ import React, {
import {
StatefulButton,
} from '@openedx/paragon';
import PropTypes from 'prop-types';

interface LoadingButtonProps {
label: string;
onClick?: (e: any) => (Promise<void> | void);
disabled?: boolean;
size?: string;
variant?: string;
className?: string;
}

/**
* A button that shows a loading spinner when clicked.
* @param {object} props
* @param {string} props.label
* @param {function=} props.onClick
* @param {boolean=} props.disabled
* @param {string=} props.size
* @param {string=} props.variant
* @param {string=} props.className
* @returns {JSX.Element}
* A button that shows a loading spinner when clicked, if the onClick function returns a Promise.
*/
const LoadingButton = ({
const LoadingButton: React.FC<LoadingButtonProps> = ({
label,
onClick,
disabled,
Expand All @@ -37,7 +36,7 @@ const LoadingButton = ({
componentMounted.current = false;
}, []);

const loadingOnClick = useCallback(async (e) => {
const loadingOnClick = useCallback(async (e: any) => {
if (!onClick) {
return;
}
Expand Down Expand Up @@ -67,21 +66,4 @@ const LoadingButton = ({
);
};

LoadingButton.propTypes = {
label: PropTypes.string.isRequired,
onClick: PropTypes.func,
disabled: PropTypes.bool,
size: PropTypes.string,
variant: PropTypes.string,
className: PropTypes.string,
};

LoadingButton.defaultProps = {
onClick: undefined,
disabled: undefined,
size: undefined,
variant: '',
className: '',
};

export default LoadingButton;
27 changes: 12 additions & 15 deletions src/generic/tag-count/index.jsx → src/generic/tag-count/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import PropTypes from 'prop-types';
import { Icon, Button } from '@openedx/paragon';
import { Button, Icon, Stack } from '@openedx/paragon';
import { Tag } from '@openedx/paragon/icons';
import classNames from 'classnames';

const TagCount = ({ count, onClick }) => {
type TagCountProps = {
count: number;
onClick?: () => void;
size?: Parameters<typeof Icon>[0]['size'];
};

// eslint-disable-next-line react/prop-types
const TagCount: React.FC<TagCountProps> = ({ count, onClick, size }) => {
const renderContent = () => (
<>
<Icon className="mr-1 pt-1" src={Tag} />
<Stack direction="horizontal" gap={1}>
<Icon size={size} src={Tag} />
{count}
</>
</Stack>
);

return (
Expand All @@ -26,13 +32,4 @@ const TagCount = ({ count, onClick }) => {
);
};

TagCount.defaultProps = {
onClick: undefined,
};

TagCount.propTypes = {
count: PropTypes.number.isRequired,
onClick: PropTypes.func,
};

export default TagCount;
Loading