Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat / Injectable component wrappers #598

Merged
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
11 changes: 7 additions & 4 deletions packages/ui-react/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import classNames from 'classnames';
import { NavLink } from 'react-router-dom';

import Spinner from '../Spinner/Spinner';
import createInjectableComponent from '../../modules/createInjectableComponent';

import styles from './Button.module.scss';

export const ButtonIdentifier = Symbol(`BUTTON`);

type Color = 'default' | 'primary' | 'delete';

type Variant = 'contained' | 'outlined' | 'text' | 'danger' | 'delete';

type Props = {
export type ButtonProps = {
children?: React.ReactNode;
label: string;
active?: boolean;
Expand All @@ -31,7 +34,7 @@ type Props = {
activeClassname?: string;
} & React.AriaAttributes;

const Button: React.FC<Props> = ({
const Button: React.FC<ButtonProps> = ({
label,
children,
color = 'default',
Expand All @@ -48,7 +51,7 @@ const Button: React.FC<Props> = ({
className,
activeClassname = '',
...rest
}: Props) => {
}: ButtonProps) => {
const buttonClassName = (isActive: boolean) =>
classNames(styles.button, className, styles[color], styles[variant], {
[styles.active]: isActive,
Expand Down Expand Up @@ -82,4 +85,4 @@ const Button: React.FC<Props> = ({
</button>
);
};
export default Button;
export default createInjectableComponent(ButtonIdentifier, Button);
7 changes: 5 additions & 2 deletions packages/ui-react/src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ import type { PosterAspectRatio } from '@jwp/ott-common/src/utils/collection';

import Image from '../Image/Image';
import Icon from '../Icon/Icon';
import createInjectableComponent from '../../modules/createInjectableComponent';

import styles from './Card.module.scss';

export const CardIdentifier = Symbol(`CARD`);

type ReplaceColon<T> = T extends `${infer Left}:${infer Right}` ? `${Left}${Right}` : T;
type PosterAspectRatioClass = ReplaceColon<PosterAspectRatio>;

type CardProps = {
export type CardProps = {
item: PlaylistItem;
onHover?: () => void;
progress?: number;
Expand Down Expand Up @@ -137,4 +140,4 @@ function Card({
);
}

export default memo(Card);
export default memo(createInjectableComponent(CardIdentifier, Card));
7 changes: 5 additions & 2 deletions packages/ui-react/src/components/CardGrid/CardGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import useBreakpoint, { Breakpoint, type Breakpoints } from '@jwp/ott-ui-react/s
import Card from '../Card/Card';
import InfiniteScrollLoader from '../InfiniteScrollLoader/InfiniteScrollLoader';
import LayoutGrid from '../LayoutGrid/LayoutGrid';
import createInjectableComponent from '../../modules/createInjectableComponent';

import styles from './CardGrid.module.scss';

Expand All @@ -26,7 +27,9 @@ const defaultCols: Breakpoints = {
[Breakpoint.xl]: 5,
};

type CardGridProps = {
export const CardGridIdentifier = Symbol(`CARD_GRID`);

export type CardGridProps = {
playlist: Playlist;
watchHistory?: { [key: string]: number };
isLoading: boolean;
Expand Down Expand Up @@ -104,4 +107,4 @@ function CardGrid({
);
}

export default CardGrid;
export default createInjectableComponent(CardGridIdentifier, CardGrid);
13 changes: 9 additions & 4 deletions packages/ui-react/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import React, { type PropsWithChildren } from 'react';
import { type PropsWithChildren } from 'react';
import classNames from 'classnames';

import createInjectableComponent from '../../modules/createInjectableComponent';

import styles from './Header.module.scss';

export const HeaderIdentifier = Symbol(`HEADER`);

type TypeHeader = 'static' | 'fixed';

type Props = {
export type HeaderProps = {
headerType?: TypeHeader;
className?: string;
searchActive: boolean;
};

const Header = ({ children, className, headerType = 'static', searchActive }: PropsWithChildren<Props>) => {
const Header = ({ children, className, headerType = 'static', searchActive }: PropsWithChildren<HeaderProps>) => {
const headerClassName = classNames(styles.header, styles[headerType], className, {
[styles.searchActive]: searchActive,
});
Expand All @@ -22,4 +26,5 @@ const Header = ({ children, className, headerType = 'static', searchActive }: Pr
</header>
);
};
export default Header;

export default createInjectableComponent(HeaderIdentifier, Header);
21 changes: 18 additions & 3 deletions packages/ui-react/src/components/MenuButton/MenuButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import React from 'react';
import { NavLink } from 'react-router-dom';
import classNames from 'classnames';

import createInjectableComponent from '../../modules/createInjectableComponent';

import styles from './MenuButton.module.scss';

type Props = {
export const MenuButtonIdentifier = Symbol(`MENU_BUTTON`);

export type MenuButtonProps = {
label?: string;
to?: string;
onClick?: () => void;
Expand All @@ -16,7 +20,18 @@ type Props = {
small?: boolean;
} & React.AriaAttributes;

const MenuButton: React.FC<Props> = ({ label, to, onClick, onBlur, onFocus, tabIndex = 0, active = false, startIcon, small = false, ...rest }: Props) => {
const MenuButton: React.FC<MenuButtonProps> = ({
label,
to,
onClick,
onBlur,
onFocus,
tabIndex = 0,
active = false,
startIcon,
small = false,
...rest
}: MenuButtonProps) => {
const icon = startIcon ? <div className={styles.startIcon}>{startIcon}</div> : null;
const getClassName = (isActive: boolean) => classNames(styles.menuButton, { [styles.small]: small }, { [styles.active]: isActive });

Expand Down Expand Up @@ -45,4 +60,4 @@ const MenuButton: React.FC<Props> = ({ label, to, onClick, onBlur, onFocus, tabI
);
};

export default MenuButton;
export default createInjectableComponent(MenuButtonIdentifier, MenuButton);
5 changes: 4 additions & 1 deletion packages/ui-react/src/components/Shelf/Shelf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import '@videodock/tile-slider/lib/style.css';

import Card from '../Card/Card';
import Icon from '../Icon/Icon';
import createInjectableComponent from '../../modules/createInjectableComponent';

import styles from './Shelf.module.scss';

Expand All @@ -34,6 +35,8 @@ export const featuredTileBreakpoints: Breakpoints = {
[Breakpoint.xl]: 1,
};

export const ShelfIdentifier = Symbol(`SHELF`);

export type ShelfProps = {
playlist: Playlist;
type: PlaylistType;
Expand Down Expand Up @@ -151,4 +154,4 @@ const Shelf = ({
);
};

export default Shelf;
export default createInjectableComponent(ShelfIdentifier, Shelf);
7 changes: 5 additions & 2 deletions packages/ui-react/src/components/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import IconButton from '../IconButton/IconButton';
import Icon from '../Icon/Icon';
import Modal, { type AnimationProps } from '../Modal/Modal';
import Slide from '../Animation/Slide/Slide';
import createInjectableComponent from '../../modules/createInjectableComponent';

import styles from './Sidebar.module.scss';

type SidebarProps = {
export const SidebarIdentifier = Symbol(`SIDEBAR`);

export type SidebarProps = {
isOpen: boolean;
onClose: () => void;
children?: ReactNode;
Expand Down Expand Up @@ -40,4 +43,4 @@ const Sidebar: React.FC<SidebarProps> = ({ isOpen, onClose, children }) => {
);
};

export default Sidebar;
export default createInjectableComponent(SidebarIdentifier, Sidebar);
2 changes: 2 additions & 0 deletions packages/ui-react/src/modules/createInjectableComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React, { type FC } from 'react';

import { container } from './container';

// This HOC is used to override a component top-level, using Inversify
// More info: platforms/web/src/modules/register.ts
const createInjectableComponent = <Props extends object>(identifier: symbol, DefaultComponent: FC<Props>) => {
return (props: Props) => {
const isOverridden = container.isBound(identifier);
Expand Down
15 changes: 8 additions & 7 deletions platforms/web/src/modules/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,18 @@ container.bind<LogTransporter>(LogTransporter).toDynamicValue(() => new ConsoleT
*
* @example
* ```ts
* // Define in types.ts (from `ui-react` for example)
* const TAG_IDENTIFIER = 'TAG_IDENTIFIER';
* // Define an identifier from the component (from `ui-react` for example):
* export const CardIdentifier = Symbol('CARD');
*
* // Bind custom component
* container.bind(TAG_IDENTIFIER).toConstantValue(CustomTag);
* // Make sure the component is wrapped in the HOC:
* export default createInjectableComponent(CardIdentifier, Card);
*
* // Then wrap the component in a HOC, from the component file itself
* export default createInjectableComponent(TAG_IDENTIFIER, DefaultTag);
* // Override the component into the associated container, from register.ts:
* import { container as uiComponentContainer } from '@jwp/ott-ui-react/src/modules/container';
*
* uiComponentContainer.bind<React.FC<CardProps>>(CardIdentifier).toConstantValue(CustomCard);
* ```
*/

// Override ui-react component
// uiComponentContainer.bind<React.FC<TagProps>>(TAG_IDENTIFIER).toConstantValue(Tag);
// uiComponentContainer.bind<React.FC<CardProps>>(CardIdentifier).toConstantValue(CustomCard);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we already used this example right above.

Loading