Skip to content

Commit

Permalink
feat(a11y): many accessibility optimisations
Browse files Browse the repository at this point in the history
fix(a11y): prevent double ids on inputs by requiring a name

feat(a11y): apply aria-modal attribute and move header landmark (#48)

feat(a11y): update button role and html structure of account and player pages (#47)

feat(a11y): add correct text markups and aria attributes (#46)

feat(home): add (geo) error message when all playlists are empty

feat(a11y): add form error announcement

feat(a11y): add solid header background color to ensure accessibility

feat(a11y): implement aria-invalid and aria-described by to inputs on error

feat(project): add google fonts from env vars

feat: keyboard accessible LayoutGrid

feat: optimize featured shelf slider for accessibility

feat(a11y): accessible sidebar & <main> landmark

feat(a11y): enhance dialog and modals accessibility

fix(a11y): alt text for images for EPG

fix(a11y): empty alt for image because of adjacent text alternative

fix(a11y): fix arrow keys for offer radio buttons

fix(a11y): skiplink first element

feat(a11y): improve html structure for VideoListItem

fix(e2e): cardgrid card navigation

feat(a11y): apply lang attribute to custom fields

feat(a11y): accessible focus outline
  • Loading branch information
langemike authored and ChristiaanScheermeijer committed Feb 8, 2024
1 parent b700fbb commit 7ffb352
Show file tree
Hide file tree
Showing 176 changed files with 3,128 additions and 1,300 deletions.
2 changes: 1 addition & 1 deletion packages/common/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const CACHE_TIME = 60 * 1000 * 20; // 20 minutes

export const STALE_TIME = 60 * 1000 * 20;

export const CARD_ASPECT_RATIOS = ['2:1', '16:9', '5:3', '4:3', '1:1', '9:13', '2:3', '9:16'] as const;
export const CARD_ASPECT_RATIOS = ['1:1', '2:1', '2:3', '4:3', '5:3', '16:9', '9:13', '9:16'] as const;

export const DEFAULT_FEATURES = {
canUpdateEmail: false,
Expand Down
10 changes: 10 additions & 0 deletions packages/common/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,35 @@ export type Env = {
APP_API_BASE_URL: string;
APP_PLAYER_ID: string;
APP_FOOTER_TEXT: string;
APP_DEFAULT_LANGUAGE: string;

APP_DEFAULT_CONFIG_SOURCE?: string;
APP_PLAYER_LICENSE_KEY?: string;

APP_BODY_FONT?: string;
APP_BODY_ALT_FONT?: string;
};

const env: Env = {
APP_VERSION: '',
APP_API_BASE_URL: 'https://cdn.jwplayer.com',
APP_PLAYER_ID: 'M4qoGvUk',
APP_FOOTER_TEXT: '',
APP_DEFAULT_LANGUAGE: 'en',
};

export const configureEnv = (options: Partial<Env>) => {
env.APP_VERSION = options.APP_VERSION || env.APP_VERSION;
env.APP_API_BASE_URL = options.APP_API_BASE_URL || env.APP_API_BASE_URL;
env.APP_PLAYER_ID = options.APP_PLAYER_ID || env.APP_PLAYER_ID;
env.APP_FOOTER_TEXT = options.APP_FOOTER_TEXT || env.APP_FOOTER_TEXT;
env.APP_DEFAULT_LANGUAGE = options.APP_DEFAULT_LANGUAGE || env.APP_DEFAULT_LANGUAGE;

env.APP_DEFAULT_CONFIG_SOURCE ||= options.APP_DEFAULT_CONFIG_SOURCE;
env.APP_PLAYER_LICENSE_KEY ||= options.APP_PLAYER_LICENSE_KEY;

env.APP_BODY_FONT = options.APP_BODY_FONT || env.APP_BODY_FONT;
env.APP_BODY_ALT_FONT = options.APP_BODY_ALT_FONT || env.APP_BODY_ALT_FONT;
};

export default env;
24 changes: 24 additions & 0 deletions packages/common/src/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ export function debounce<T extends (...args: any[]) => void>(callback: T, wait =
timeout = setTimeout(() => callback(...args), wait);
};
}
export function throttle<T extends (...args: any[]) => unknown>(func: T, limit: number): (...args: Parameters<T>) => void {
let lastFunc: NodeJS.Timeout | undefined;
let lastRan: number | undefined;

return function (this: ThisParameterType<T>, ...args: Parameters<T>): void {
const timeSinceLastRan = lastRan ? Date.now() - lastRan : limit;

if (timeSinceLastRan >= limit) {
func.apply(this, args);
lastRan = Date.now();
} else if (!lastFunc) {
lastFunc = setTimeout(() => {
if (lastRan) {
const timeSinceLastRan = Date.now() - lastRan;
if (timeSinceLastRan >= limit) {
func.apply(this, args);
lastRan = Date.now();
}
}
lastFunc = undefined;
}, limit - timeSinceLastRan);
}
};
}

export const unicodeToChar = (text: string) => {
return text.replace(/\\u[\dA-F]{4}/gi, (match) => {
Expand Down
73 changes: 73 additions & 0 deletions packages/hooks-react/src/usePlaylists.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { PersonalShelf, PersonalShelves, PLAYLIST_LIMIT } from '@jwp/ott-common/src/constants';
import ApiService from '@jwp/ott-common/src/services/ApiService';
import { getModule } from '@jwp/ott-common/src/modules/container';
import { useFavoritesStore } from '@jwp/ott-common/src/stores/FavoritesStore';
import { useWatchHistoryStore } from '@jwp/ott-common/src/stores/WatchHistoryStore';
import { generatePlaylistPlaceholder } from '@jwp/ott-common/src/utils/collection';
import { isTruthyCustomParamValue } from '@jwp/ott-common/src/utils/common';
import { isScheduledOrLiveMedia } from '@jwp/ott-common/src/utils/liveEvent';
import type { Content } from '@jwp/ott-common/types/config';
import type { Playlist } from '@jwp/ott-common/types/playlist';
import { useQueries, useQueryClient } from 'react-query';

const placeholderData = generatePlaylistPlaceholder(30);

type UsePlaylistResult = {
data: Playlist | undefined;
isLoading: boolean;
isSuccess?: boolean;
error?: unknown;
}[];

const usePlaylists = (content: Content[], rowsToLoad: number | undefined = undefined) => {
const page_limit = PLAYLIST_LIMIT.toString();
const queryClient = useQueryClient();
const apiService = getModule(ApiService);

const favorites = useFavoritesStore((state) => state.getPlaylist());
const watchHistory = useWatchHistoryStore((state) => state.getPlaylist());

const playlistQueries = useQueries(
content.map(({ contentId, type }, index) => ({
enabled: !!contentId && (!rowsToLoad || rowsToLoad > index) && !PersonalShelves.some((pType) => pType === type),
queryKey: ['playlist', contentId],
queryFn: async () => {
const playlist = await apiService.getPlaylistById(contentId, { page_limit });

// This pre-caches all playlist items and makes navigating a lot faster.
playlist?.playlist?.forEach((playlistItem) => {
queryClient.setQueryData(['media', playlistItem.mediaid], playlistItem);
});

return playlist;
},
placeholderData: !!contentId && placeholderData,
refetchInterval: (data: Playlist | undefined) => {
if (!data) return false;

const autoRefetch = isTruthyCustomParamValue(data.refetch) || data.playlist.some(isScheduledOrLiveMedia);

return autoRefetch ? 1000 * 30 : false;
},
retry: false,
})),
);

const result: UsePlaylistResult = content.map(({ type }, index) => {
if (type === PersonalShelf.Favorites) return { data: favorites, isLoading: false, isSuccess: true };
if (type === PersonalShelf.ContinueWatching) return { data: watchHistory, isLoading: false, isSuccess: true };

const { data, isLoading, isSuccess, error } = playlistQueries[index];

return {
data,
isLoading,
isSuccess,
error,
};
});

return result;
};

export default usePlaylists;
Loading

0 comments on commit 7ffb352

Please sign in to comment.