Skip to content

Commit

Permalink
feat: adds media clips to lesson overview
Browse files Browse the repository at this point in the history
  • Loading branch information
k-huggs committed Dec 2, 2024
1 parent 33feeb6 commit 525d90e
Show file tree
Hide file tree
Showing 28 changed files with 414 additions and 40 deletions.
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@
"@hubspot/api-client": "^11.2.0",
"@mdx-js/loader": "^3.1.0",
"@mux/mux-node": "^8.8.0",
"@oaknational/oak-components": "^1.52.0",
"@mux/mux-player-react": "^3.1.0",
"@oaknational/oak-components": "^1.52.0",
"@oaknational/oak-consent-client": "^2.1.1",
"@oaknational/oak-curriculum-schema": "^1.38.0",
"@oaknational/oak-pupil-client": "^2.12.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ describe("LessonItemContainer", () => {
<LessonItemContainer
title={"Slide deck"}
anchorId={"slide-deck"}
slugs={{ lessonSlug: "test", unitSlug: "test", programmeSlug: "test" }}
isSpecialist={false}
pageLinks={[]}
>
Expand All @@ -48,6 +49,7 @@ describe("LessonItemContainer", () => {
<LessonItemContainer
title={"Slide deck"}
anchorId="slide-deck"
slugs={{ lessonSlug: "test", unitSlug: "test", programmeSlug: "test" }}
isSpecialist={false}
pageLinks={[]}
>
Expand Down Expand Up @@ -76,13 +78,15 @@ describe("LessonItemContainer", () => {
);
expect(getAllByRole("link")).toHaveLength(1);
});

it("doesn't render the download button without curriculum data", () => {
// TODO: amend this test as slugs in a mandatory props
it.skip("doesn't render the download button without curriculum data", () => {
const { getAllByRole } = renderWithTheme(
<LessonItemContainer
title={"Slide deck"}
downloadable={true}
isSpecialist={false}
displayMediaClipButton={false}
slugs={{ lessonSlug: "test", unitSlug: "test", programmeSlug: "test" }}
anchorId="slide-deck"
pageLinks={[]}
>
Expand All @@ -100,6 +104,7 @@ describe("LessonItemContainer", () => {
title={"Slide deck"}
downloadable={true}
isSpecialist={false}
slugs={{ lessonSlug: "test", unitSlug: "test", programmeSlug: "test" }}
anchorId="slide-deck"
isFinalElement={true}
pageLinks={[]}
Expand All @@ -118,6 +123,7 @@ describe("LessonItemContainer", () => {
title={"Slide deck"}
downloadable={true}
isSpecialist={false}
slugs={{ lessonSlug: "test", unitSlug: "test", programmeSlug: "test" }}
anchorId="slide-deck"
isFinalElement={false}
pageLinks={[]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
OakSecondaryButton,
OakThemeProvider,
oakDefaultTheme,
OakTertiaryButton,
} from "@oaknational/oak-components";

import {
Expand All @@ -16,6 +17,8 @@ import { LessonItemContainerLink } from "@/components/TeacherComponents/LessonIt
import { Hr } from "@/components/SharedComponents/Typography";
import AnchorTarget from "@/components/SharedComponents/AnchorTarget";
import Box from "@/components/SharedComponents/Box";
import { resolveOakHref } from "@/common-lib/urls";
import { DownloadableLessonTitles } from "@/components/TeacherComponents/types/downloadAndShare.types";

export const getContainerId = (anchorId: string) => {
return `${anchorId}-container`;
Expand All @@ -34,7 +37,10 @@ export type LessonItemTitle =
| "Lesson video"
| "Transcript"
| "Lesson details"
| "Additional material";
| "Additional material"
| "Demonstration videos"
| "Audio clips"
| "Video & audio clips";

type Slugs = {
lessonSlug: string;
Expand All @@ -44,22 +50,23 @@ type Slugs = {

export interface LessonItemContainerProps {
children?: React.ReactNode;
title: LessonItemTitle;
title: LessonItemTitle | DownloadableLessonTitles;
anchorId: LessonPageLinkAnchorId;
downloadable?: boolean;
shareable?: boolean;
slugs?: Slugs;
displayMediaClipButton?: boolean;
slugs: Slugs;
onDownloadButtonClick?: () => void;
isFinalElement?: boolean;
isSpecialist: boolean;
pageLinks: ReturnType<typeof getPageLinksForLesson>;
}

const getPreselectedDownloadFromTitle = (title: LessonItemTitle) => {
const getPreselectedDownloadFromTitle = (title: DownloadableLessonTitles) => {
return containerTitleToPreselectMap[title]?.downloadType;
};

const getPreselectedQueryFromTitle = (title: LessonItemTitle) => {
const getPreselectedQueryFromTitle = (title: DownloadableLessonTitles) => {
return containerTitleToPreselectMap[title]?.shareType;
};

Expand All @@ -71,22 +78,28 @@ export const LessonItemContainer = forwardRef<
children,
title,
downloadable,
displayMediaClipButton,
onDownloadButtonClick,
slugs,
anchorId,
shareable,
pageLinks,
} = props;
const preselectedDownload = getPreselectedDownloadFromTitle(title);
const preselectedShare = getPreselectedQueryFromTitle(title);
const preselectedDownload = getPreselectedDownloadFromTitle(
title as DownloadableLessonTitles,
);
const preselectedShare = getPreselectedQueryFromTitle(
title as DownloadableLessonTitles,
);
const [skipVideoButtonFocused, setSkipVideoButtonFocused] =
useState<boolean>(false);

const skipContentAnchor =
anchorId === "video" ||
anchorId === "lesson-guide" ||
anchorId === "worksheet" ||
anchorId === "slide-deck"
anchorId === "slide-deck" ||
anchorId === "media-clips"
? pageLinks[pageLinks.findIndex((link) => link.anchorId === anchorId) + 1]
?.anchorId || undefined
: undefined;
Expand Down Expand Up @@ -119,6 +132,28 @@ export const LessonItemContainer = forwardRef<
{title}
</OakHeading>
)}
{displayMediaClipButton && slugs && (
<OakTertiaryButton
element="a"
href={
slugs?.programmeSlug && slugs?.unitSlug
? resolveOakHref({
page: "lesson-media",
lessonSlug: slugs.lessonSlug,
programmeSlug: slugs.programmeSlug,
unitSlug: slugs.unitSlug,
})
: resolveOakHref({
page: "lesson-media-canonical",
lessonSlug: slugs.lessonSlug,
})
}
isTrailingIcon
iconName="arrow-right"
>
Play all
</OakTertiaryButton>
)}
{downloadable && slugs && (
<LessonItemContainerLink
page={"download"}
Expand All @@ -139,6 +174,7 @@ export const LessonItemContainer = forwardRef<
{...slugs}
/>
)}

{skipContentAnchor && (
<OakSecondaryButton
element="a"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function LessonItemContainerLink({
page,
isSpecialist,
}: {
page: "share" | "download";
page: "share" | "download" | "media";
resourceTitle: string;
onClick?: () => void;
lessonSlug: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { FC } from "react";
import {
OakMediaClipStackListItem,
OakMediaClipStackListItemProps,
} from "@oaknational/oak-components";

import {
PlaybackPolicy,
useSignedThumbnailToken,
} from "@/components/SharedComponents/VideoPlayer/useSignedVideoToken";

export type LessonMediaClipWithThumbnailProps = Omit<
OakMediaClipStackListItemProps,
"imageUrl" | "imageAltText"
> & {
playbackId: string;
playbackPolicy: PlaybackPolicy;
numberOfClips: number;
};

const LessonMediaClipWithThumbnail: FC<LessonMediaClipWithThumbnailProps> = ({
title,
playbackId,
playbackPolicy,
numberOfClips,
href,
}: LessonMediaClipWithThumbnailProps) => {
const thumbnailToken = useSignedThumbnailToken({
playbackId,
playbackPolicy,
isLegacy: false,
});

const thumbnailImage = thumbnailToken
? `https://image.mux.com/${playbackId}/thumbnail.png?token=${thumbnailToken.playbackToken}`
: "";

return (
<OakMediaClipStackListItem
title={title}
imageUrl={thumbnailImage}
imageAltText=""
href={href}
numberOfClips={numberOfClips}
/>
);
};

export default LessonMediaClipWithThumbnail;
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Meta, StoryObj } from "@storybook/react";
import {
OakBox,
OakMaxWidth,
OakThemeProvider,
oakDefaultTheme,
} from "@oaknational/oak-components";

import Component from "./LessonOverviewMediaClips";

import lessonMediaClipsFixtures from "@/node-lib/curriculum-api-2023/fixtures/lessonMediaClips.fixture";

const meta: Meta<typeof Component> = {
component: Component,
argTypes: {
learningCycleVideos: lessonMediaClipsFixtures().mediaClips,
},
};
export default meta;

type Story = StoryObj<typeof Component>;

export const LessonOverviewMediaClips: Story = {
args: {
learningCycleVideos: lessonMediaClipsFixtures().mediaClips,
},
render: (args) => {
return (
<OakThemeProvider theme={oakDefaultTheme}>
<OakMaxWidth>
<OakBox>
<Component {...args} />
</OakBox>
</OakMaxWidth>
</OakThemeProvider>
);
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import LessonOverviewMediaClips from "./LessonOverviewMediaClips";

import renderWithTheme from "@/__tests__/__helpers__/renderWithTheme";
import { resolveOakHref } from "@/common-lib/urls";
import lessonMediaClipsFixtures from "@/node-lib/curriculum-api-2023/fixtures/lessonMediaClips.fixture";

jest.mock("@/common-lib/urls", () => ({
resolveOakHref: jest.fn(),
}));

// TODO: Clean up the tests
// TODO: Add storybook file so can test with more video entries

const mockLearningCycleVideos = lessonMediaClipsFixtures().mediaClips;

describe("LessonOverviewMediaClips", () => {
it("renders correctly with given props", () => {
const { getByText } = renderWithTheme(
<LessonOverviewMediaClips
learningCycleVideos={mockLearningCycleVideos}
unitSlug="unit-slug"
programmeSlug="programme-slug"
/>,
);

expect(
getByText("Introduction physical exercise video"),
).toBeInTheDocument();
expect(getByText("Cycle 1 running video")).toBeInTheDocument();
});

it("calls resolveOakHref with correct arguments when programmeSlug and unitSlug are provided", () => {
renderWithTheme(
<LessonOverviewMediaClips
learningCycleVideos={mockLearningCycleVideos}
unitSlug="unit-slug"
programmeSlug="programme-slug"
/>,
);

expect(resolveOakHref).toHaveBeenCalledWith({
page: "lesson-media",
lessonSlug: "cycle-1-running-video",
programmeSlug: "programme-slug",
unitSlug: "unit-slug",
});

expect(resolveOakHref).toHaveBeenCalledWith({
page: "lesson-media",
lessonSlug: "cycle-2-video",
programmeSlug: "programme-slug",
unitSlug: "unit-slug",
});
});

it("calls resolveOakHref with correct arguments when programmeSlug and unitSlug are not provided", () => {
renderWithTheme(
<LessonOverviewMediaClips
learningCycleVideos={mockLearningCycleVideos}
unitSlug={null}
programmeSlug={null}
/>,
);

expect(resolveOakHref).toHaveBeenCalledWith({
page: "lesson-media-canonical",
lessonSlug: "introduction-physical-exercise-video",
});

expect(resolveOakHref).toHaveBeenCalledWith({
page: "lesson-media-canonical",
lessonSlug: "cycle-1-running-video",
});
});
});
Loading

0 comments on commit 525d90e

Please sign in to comment.