Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
4 changes: 3 additions & 1 deletion src/course-outline/card-header/TitleButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ const TitleButton = ({
onClick={onTitleClick}
title={title}
>
{prefixIcon}
<div className="mr-2">
{prefixIcon}
</div>
<span className={`${namePrefix}-card-title mb-0 truncate-1-line`}>
{title}
</span>
Expand Down
30 changes: 17 additions & 13 deletions src/course-outline/card-header/TitleLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,23 @@ const TitleLink = ({
namePrefix,
prefixIcon,
}: TitleLinkProps) => (
<Button
as={Link}
variant="tertiary"
data-testid={`${namePrefix}-card-header__title-link`}
className="item-card-header__title-btn align-items-end"
to={titleLink}
title={title}
>
{prefixIcon}
<span className={`${namePrefix}-card-title mb-0 truncate-1-line`}>
{title}
</span>
</Button>
<>
<div className="mr-2">
{prefixIcon}
</div>
<Button
as={Link}
variant="tertiary"
data-testid={`${namePrefix}-card-header__title-link`}
className="item-card-header__title-btn align-items-end"
to={titleLink}
title={title}
>
<span className={`${namePrefix}-card-title mb-0 truncate-1-line text-left`}>
{title}
</span>
</Button>
</>
);

export default TitleLink;
1 change: 1 addition & 0 deletions src/course-outline/section-card/SectionCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const section = {
versionDeclined: null,
errorMessage: null,
downstreamCustomized: [] as string[],
upstreamName: 'Upstream',
},
} satisfies Partial<XBlock> as XBlock;

Expand Down
8 changes: 7 additions & 1 deletion src/course-outline/section-card/SectionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,13 @@ const SectionCard = ({
isExpanded={isExpanded}
onTitleClick={handleExpandContent}
namePrefix={namePrefix}
prefixIcon={<UpstreamInfoIcon upstreamInfo={upstreamInfo} />}
prefixIcon={(
<UpstreamInfoIcon
upstreamInfo={upstreamInfo}
size="md"
openSyncModal={openSyncModal}
/>
)}
/>
);

Expand Down
1 change: 1 addition & 0 deletions src/course-outline/subsection-card/SubsectionCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ const subsection: XBlock = {
versionDeclined: null,
errorMessage: null,
downstreamCustomized: [] as string[],
upstreamName: 'Upstream',
},
} satisfies Partial<XBlock> as XBlock;

Expand Down
8 changes: 7 additions & 1 deletion src/course-outline/subsection-card/SubsectionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,13 @@ const SubsectionCard = ({
isExpanded={isExpanded}
onTitleClick={handleExpandContent}
namePrefix={namePrefix}
prefixIcon={<UpstreamInfoIcon upstreamInfo={upstreamInfo} />}
prefixIcon={(
<UpstreamInfoIcon
upstreamInfo={upstreamInfo}
size="sm"
openSyncModal={openSyncModal}
/>
)}
/>
);

Expand Down
1 change: 1 addition & 0 deletions src/course-outline/unit-card/UnitCard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
font-weight: var(--pgn-typography-headings-font-weight);
line-height: var(--pgn-typography-headings-line-height);
color: var(--pgn-color-headings-base);
align-self: center;
}
}
1 change: 1 addition & 0 deletions src/course-outline/unit-card/UnitCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const unit = {
versionDeclined: null,
errorMessage: null,
downstreamCustomized: [] as string[],
upstreamName: 'Upstream',
},
} satisfies Partial<XBlock> as XBlock;

Expand Down
8 changes: 7 additions & 1 deletion src/course-outline/unit-card/UnitCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,13 @@ const UnitCard = ({
title={displayName}
titleLink={getTitleLink(id)}
namePrefix={namePrefix}
prefixIcon={<UpstreamInfoIcon upstreamInfo={upstreamInfo} size="sm" />}
prefixIcon={(
<UpstreamInfoIcon
upstreamInfo={upstreamInfo}
size="xs"
openSyncModal={openSyncModal}
/>
)}
/>
);

Expand Down
1 change: 1 addition & 0 deletions src/data/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface UpstreamChildrenInfo {
export interface UpstreamInfo {
readyToSync: boolean,
upstreamRef: string,
upstreamName: string,
versionSynced: number,
versionAvailable: number | null,
versionDeclined: number | null,
Expand Down
1 change: 1 addition & 0 deletions src/generic/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
@import "./modal-iframe";
@import "./alert-message";
@import "./inplace-text-editor/InplaceTextEditor";
@import "./upstream-info-icon/UpstreamInfoIcon";
42 changes: 42 additions & 0 deletions src/generic/upstream-info-icon/UpstreamInfoIcon.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
.upstream-info-icon {
border: 1px solid var(--pgn-color-light-800);
color: var(--pgn-color-primary-500);

&.sync-state {
&:hover {
border-color: var(--pgn-color-primary-500);
background-color: var(--pgn-color-primary-500);
color: white;
}
}

&.size-one-md {
width: 32px;
height: 26px;
}

&.size-one-sm {
width: 28px;
height: 22px;
}

&.size-one-xs {
width: 24px;
height: 18px;
}

&.size-two-md {
width: 60px;
height: 26px;
}

&.size-two-sm {
width: 46px;
height: 22px;
}

&.size-two-xs {
width: 36px;
height: 18px;
}
}
90 changes: 80 additions & 10 deletions src/generic/upstream-info-icon/UpstreamInfoIcon.test.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,105 @@
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { render, screen } from '@testing-library/react';
import {
render, screen, fireEvent, waitFor, initializeMocks,
} from '@src/testUtils';
import { UpstreamInfoIcon, UpstreamInfoIconProps } from '.';

type UpstreamInfo = UpstreamInfoIconProps['upstreamInfo'];
const mockOpenSyncModal = jest.fn();

const renderComponent = (upstreamInfo?: UpstreamInfo) => (
render(
<IntlProvider locale="en">
<UpstreamInfoIcon upstreamInfo={upstreamInfo} />
</IntlProvider>,
<UpstreamInfoIcon upstreamInfo={upstreamInfo} openSyncModal={mockOpenSyncModal} />,
)
);

describe('<UpstreamInfoIcon>', () => {
beforeEach(() => {
initializeMocks();
});

it('should render with link', () => {
renderComponent({ upstreamRef: 'some-ref', errorMessage: null });
renderComponent({
upstreamRef: 'some-ref',
errorMessage: null,
readyToSync: false,
downstreamCustomized: [],
upstreamName: 'Upstream',
});
expect(screen.getByTitle('This item is linked to a library item.')).toBeInTheDocument();
expect(screen.queryByTitle('The linked library object has updates available.')).not.toBeInTheDocument();
});

it('should render with broken link', () => {
renderComponent({ upstreamRef: 'some-ref', errorMessage: 'upstream error' });
expect(screen.getByTitle('The link to the library item is broken.')).toBeInTheDocument();
renderComponent({
upstreamRef: 'some-ref',
errorMessage: 'upstream error',
readyToSync: false,
downstreamCustomized: [],
upstreamName: 'Upstream',
});
expect(screen.getByTitle('This item is linked to a library item.')).toBeInTheDocument();
expect(screen.getByTitle('The referenced library or library object is not available.')).toBeInTheDocument();
});

it('should render with ready to sync link and opens the sync modal', async () => {
renderComponent({
upstreamRef: 'some-ref',
errorMessage: null,
readyToSync: true,
downstreamCustomized: [],
upstreamName: 'Upstream',
});

const icon = screen.getByTitle('This item is linked to a library item.');
expect(icon).toBeInTheDocument();
expect(screen.getByTitle('The linked library object has updates available.')).toBeInTheDocument();

fireEvent.click(icon);
await waitFor(() => expect(mockOpenSyncModal).toHaveBeenCalled());
});

it('should render with course overrides', () => {
renderComponent({
upstreamRef: 'some-ref',
errorMessage: null,
readyToSync: false,
downstreamCustomized: ['data'],
upstreamName: 'Upstream',
});

expect(screen.getByTitle('This item is linked to a library item.')).toBeInTheDocument();
expect(screen.getByTitle('This library reference has course overrides applied.')).toBeInTheDocument();
});

it('should render with ready to sync and course overrides', () => {
renderComponent({
upstreamRef: 'some-ref',
errorMessage: null,
readyToSync: true,
downstreamCustomized: ['data'],
upstreamName: 'Upstream',
});

expect(screen.getByTitle('This item is linked to a library item.')).toBeInTheDocument();
expect(screen.queryByTitle('This library reference has course overrides applied.')).not.toBeInTheDocument();
expect(screen.getByTitle('The linked library object has updates available.')).toBeInTheDocument();
});

it('should render null without upstream', () => {
const { container } = renderComponent(undefined);
renderComponent(undefined);
const container = screen.getByTestId('redux-provider');
expect(container).toBeEmptyDOMElement();
});

it('should render null without upstreamRf', () => {
const { container } = renderComponent({ upstreamRef: null, errorMessage: null });
renderComponent({
upstreamRef: null,
errorMessage: null,
readyToSync: false,
downstreamCustomized: [],
upstreamName: 'Upstream',
});
const container = screen.getByTestId('redux-provider');
expect(container).toBeEmptyDOMElement();
});
});
Loading