Wikia's reusable React parts.

1. Add it to your project

1.1. Change wikia scoped packages to use artifactory

This setup will work for both yarn and npm. If you run into issues consult Artifactory NPM Docs first

$> npm config set @wikia:registry https://artifactory.wikia-inc.com/artifactory/api/npm/wikia-npm/
$> npm config set @fandom:registry https://artifactory.wikia-inc.com/artifactory/api/npm/wikia-npm/

1.2. Add the package to package.json

$> yarn add @wikia/react-common

or

$> npm i @wikia/react-common

2. Use it in the code

import Button from '@wikia/react-common/components/Button';
import IconAdd from '@wikia/react-common/icons/IconAdd';

...

<div>
    <Button>
        <IconAdd />
        Camera
    </Button>
</div>

3. Add the CSS to your build

Make sure you include the CSS in your styles.

@import "~@wikia/react-common/components/Icon.css";
@import "~@wikia/react-common/components/Button.css";

[IMPORTANT] Artifactory Setup

This setup will work for both yarn and npm. If you run into issues consult Artifactory NPM Docs first

  1. Verify you can login to Artifactory Wikia
  2. Change wikia scoped packages to use artifactory
    • $> npm config set @wikia:registry https://artifactory.wikia-inc.com/artifactory/api/npm/wikia-npm/
  3. Login
    • $> npm login --scope=@wikia
    • Use artifactory login and artifactory API key for password by going to "Edit Profile" in artifactory web ui

General guidelines

  • ES6 React components with prop-types saved in .js file.
  • Use function syntax if possible, use nesting and flat files.
  • 100% lint and coverage and no regressions
  • use Jest as a general testing framework and for testing component's rendering
  • use Enzyme for testing interactions
  • use Sinon for testing callbacks

Step-by-step guide for components

  1. Assuming the new component's name is ComponentA all it's files will be in /source/components/ComponentA/ directory.
  2. Create component in ComponentA/index.js.
  3. Add component to /source/components/index.js.
  4. Add component to /config/styleguide.config.json.
  5. (optionally) create styles in ComponentA/styles.s?css and import them in ComponentA/index.js.
  6. Document the usage and props in JSDocs in ComponentA/index.js.
  7. Add example or examples in ComponentA/README.md.
  8. Add unit test in ComponentA/index.spec.js, aim for 100% coverage and all the test cases.
  9. Create new Pull Request.
  10. Code will be merged to master only if there are no regressions and after a successful CR.
  11. When the code is merged to master, release new version of the styleguide with one of the release commands.

HOCS

  1. Higher order components (hoc) can be added by following the guide

Note: The one difference will be to use js static in the readme to prevent rendering as styleguidist doesn't have access to the hoc

Development server

> yarn dev

Tests

The easiest way is to run the full suite:

> yarn ci

It will run linting (ESLint, Stylelint), Jest and will output coverage report.

Watch

There's a command for watching Jest tests:

> yarn test:watch

Build

Running the build is as simple as:

> yarn build

This will run few build commands in sequence:

  1. Remove every generated file and directory from the dist/ directory (equivalent of yarn clean).
  2. Build the library outputting built ES5 files to the dist/ (yarn lib:build).
  3. Build the styleguide in the docs/ directory; it contains the build styleguide that will appear on the GitHub pages (yarn styleguide:build).

Release

After PR is merged into master branch create new release. You should use SemVer using one of the following commands.

The script will automatically pull newest master branch, build the documentation, create new release version in the package.json, create GitHub tag and push this tag to GitHub. After pushing the new tag it will publish the package to artifactory.

Default release; bugfixes, no new features and no breaking changes

> yarn release

New features, but no breaking changes

> yarn release:minor

Breaking changes, regardless how small

> yarn release:major

Theme values are provided in two different ways:

Theme object

The main theme is just an object with values than can be easily used in plain JavaScript:

Can be used in plain JS

import theme from '@wikia/react-common/theme';

console.log(theme.color.fandom_aqua); // outputs '#00d6d6'
console.log(theme.color.font_size.l); // outputs '18px'

But it's also a ThemeProvider-ready:

import theme from '@wikia/react-common/theme';

const AquaText = styled.span`
    color: ${props => props.theme.color.fandom_aqua};
    font-family: ${props => props.theme.font_family};
    font-size: ${props => props.theme.font_size.l};

    @media ${props => props.theme.media.large_up} {
        font-size: ${props => props.theme.font_size.xl}
    }
`;

export default () => (
    <ThemeProvider theme={theme}>
        <AquaText>O hai</AquaText>
    </ThemeProvider>
);

Theme object structure

Here's how the object looks like (values omitted):

const theme = {
    breakpoint_values: /* .. */,
    breakpoint_available_width: /* .. */,
    breakpoint_available_width_values: /* same as `breakpoint_available_width` without units */,
    color: /* .. */,
    color_theme: {
        light: /* .. */,
        dark: /* .. */,
    },
    content_well_margin: /* ... */,
    content_well_margin_value: /* same as `content_well` without units */,
    font_family: /* .. */,
    font_size: /* .. */,
    font_size_value: /* same as `font_size` without units */,
    font_weight: /* ... */,
    line_height: /* ... */,
    media: /* ... */,
};

You can see theme's values in the source.

Changing theme (customization)

If you want to change/override or add new values to the theme, there's a handy function for that:

import generateTheme from '@wikia/react-common/theme/generateTheme';

const customTheme = generateTheme({
    color: {
        link: 'blue',
    },
});

Separate theme values

If you don't need the object, there are separate values available:

Breakpoints

The @wikia/react-common/theme/breakpoints exports the following consts:

  • BREAKPOINTS
  • MEDIAQUERIES
  • CONTENT_WELL_MARGIN
  • CONTENT_WELL_MARGIN_PX
  • BREAKPOINTS_AVAILABLE_WIDTH
  • BREAKPOINTS_AVAILABLE_WIDTH_PX

Colors

The @wikia/react-common/theme/colors exports the following consts:

  • COLORS
  • COLORS_LIGHT_THEME
  • COLORS_DARK_THEME

Typography

The @wikia/react-common/theme/typography exports the following consts:

  • FONT_FAMILY
  • LINE_HEIGHTS
  • FONT_WEIGHTS
  • FONT_SIZES
  • FONT_SIZES_PX

There are three basic icon sizes: regular (24px), small (18px) and tiny (12px). Every icon component has size prop that can modify it's height and width; in addition every icon's fill color is defaulted to current color.

Usage:

@import <IconComponent> from '@wikia/react-common/icons/<IconComponent>';

Here's the list of all the icons in different sizes; hover over icon to see component's name

IconActivity
IconActivity
IconActivitySmall
IconActivityTiny
IconAdd
IconAdd
IconAddSmall
IconAddTiny
IconAlert
IconAlert
IconAlertSmall
IconAlertTiny
IconArrow
IconArrow
IconArrowSmall
IconArrowTiny
IconAvatar
IconAvatar
IconAvatarSmall
IconAvatarTiny
IconBell
IconBell
IconBellSmall
IconBellTiny
IconBlocks
IconBlocks
IconBlocksSmall
IconBlocksTiny
IconBold
IconBold
IconBoldSmall
IconBoldTiny
IconBoldActive
IconBoldActive
IconBoldActiveSmall
IconBoldActiveTiny
IconBook
IconBook
IconBookSmall
IconBookTiny
IconBookmark
IconBookmark
IconBookmarkSmall
IconBookmarkTiny
IconBubble
IconBubble
IconBubbleSmall
IconBubbleTiny
IconBulletList
IconBulletList
IconBulletListSmall
IconBulletListTiny
IconCalendar
IconCalendar
IconCalendarSmall
IconCalendarTiny
IconCamera
IconCamera
IconCameraSmall
IconCameraTiny
IconCheckbox
IconCheckbox
IconCheckboxSmall
IconCheckboxTiny
IconCheckboxEmpty
IconCheckboxEmpty
IconCheckboxEmptySmall
IconCheckboxEmptyTiny
IconCheckmark
IconCheckmark
IconCheckmarkSmall
IconCheckmarkTiny
IconClipboard
IconClipboard
IconClipboardSmall
IconClipboardTiny
IconClock
IconClock
IconClockSmall
IconClockTiny
IconClose
IconClose
IconCloseSmall
IconCloseTiny
IconComment
IconComment
IconCommentSmall
IconCommentTiny
IconControls
IconControls
IconControlsSmall
IconControlsTiny
IconCross
IconCross
IconCrossSmall
IconCrossTiny
IconDashboard
IconDashboard
IconDashboardSmall
IconDashboardTiny
IconDiscord
IconDiscord
IconDownload
IconDownload
IconDownloadSmall
IconDownloadTiny
IconDropdown
IconDropdown
IconDropdownSmall
IconDropdownTiny
IconEnvelope
IconEnvelope
IconEnvelopeSmall
IconEnvelopeTiny
IconError
IconError
IconErrorSmall
IconErrorTiny
IconExternal
IconExternal
IconExternalSmall
IconExternalTiny
IconEye
IconEye
IconEyeSmall
IconEyeTiny
IconEyeCrossed
IconEyeCrossed
IconEyeCrossedSmall
IconEyeCrossedTiny
IconFacebook
IconFacebook
IconFacebookSmall
IconFacebookTiny
IconFandom
IconFandom
IconFandomSmall
IconFandomTiny
IconFlag
IconFlag
IconFlagSmall
IconFlagTiny
IconFootnote
IconFootnote
IconFootnoteSmall
IconFootnoteTiny
IconGear
IconGear
IconGearSmall
IconGearTiny
IconGoogleplus
IconGoogleplus
IconGrid
IconGrid
IconGridSmall
IconGridTiny
IconGripper
IconGripper
IconGripperSmall
IconGripperTiny
IconH2
IconH2
IconH2Small
IconH2Tiny
IconH3
IconH3
IconH3Small
IconH3Tiny
IconHeart
IconHeart
IconHeartSmall
IconHeartTiny
IconHeartFilled
IconHeartFilled
IconHeartFilledSmall
IconHeartFilledTiny
IconHome
IconHome
IconHomeSmall
IconHomeTiny
IconImage
IconImage
IconImageSmall
IconImageTiny
IconImages
IconImages
IconImagesSmall
IconImagesTiny
IconIndentLeft
IconIndentLeft
IconIndentLeftSmall
IconIndentLeftTiny
IconIndentRight
IconIndentRight
IconIndentRightSmall
IconIndentRightTiny
IconInfo
IconInfo
IconInfoSmall
IconInfoTiny
IconInstagram
IconInstagram
IconItalics
IconItalics
IconItalicsSmall
IconItalicsTiny
IconLeftAlign
IconLeftAlign
IconLeftAlignSmall
IconLeftAlignTiny
IconLine
IconLine
IconLink
IconLink
IconLinkSmall
IconLinkTiny
IconLinkedin
IconLinkedin
IconLoading
IconLoading
IconLock
IconLock
IconLockSmall
IconLockTiny
IconMagnifyingGlass
IconMagnifyingGlass
IconMagnifyingGlassSmall
IconMagnifyingGlassTiny
IconMap
IconMap
IconMapSmall
IconMapTiny
IconMeneame
IconMeneame
IconMention
IconMention
IconMentionSmall
IconMentionTiny
IconMenu
IconMenu
IconMenuSmall
IconMenuTiny
IconMenuControl
IconMenuControl
IconMenuControlSmall
IconMenuControlTiny
IconMessage
IconMessage
IconMessageSmall
IconMessageTiny
IconMore
IconMore
IconMoreSmall
IconMoreTiny
IconMove
IconMove
IconMoveSmall
IconMoveTiny
IconNk
IconNk
IconNumberedList
IconNumberedList
IconNumberedListSmall
IconNumberedListTiny
IconOdnoklassniki
IconOdnoklassniki
IconPage
IconPage
IconPageSmall
IconPageTiny
IconPageList
IconPageList
IconPageListSmall
IconPageListTiny
IconPages
IconPages
IconPagesSmall
IconPagesTiny
IconParagraph
IconParagraph
IconParagraphSmall
IconParagraphTiny
IconPause
IconPause
IconPauseSmall
IconPauseTiny
IconPencil
IconPencil
IconPencilSmall
IconPencilTiny
IconPin
IconPin
IconPinSmall
IconPinTiny
IconPlay
IconPlay
IconPlaySmall
IconPlayTiny
IconPoll
IconPoll
IconPollSmall
IconPollTiny
IconPreformat
IconPreformat
IconPreformatSmall
IconPreformatTiny
IconQuestion
IconQuestion
IconQuestionSmall
IconQuestionTiny
IconQuiz
IconQuiz
IconQuizSmall
IconQuizTiny
IconQuote
IconQuote
IconQuoteSmall
IconQuoteTiny
IconQzone
IconQzone
IconRadioActive
IconRadioActive
IconRadioActiveSmall
IconRadioActiveTiny
IconRadioEmpty
IconRadioEmpty
IconRadioEmptySmall
IconRadioEmptyTiny
IconReddit
IconReddit
IconRedditSmall
IconRedditTiny
IconRefresh
IconRefresh
IconRefreshSmall
IconRefreshTiny
IconReply
IconReply
IconReplySmall
IconReplyTiny
IconShare
IconShare
IconShareSmall
IconShareTiny
IconSitemap
IconSitemap
IconSitemapSmall
IconSitemapTiny
IconSound
IconSound
IconSoundSmall
IconSoundTiny
IconSoundOff
IconSoundOff
IconSoundOffSmall
IconSoundOffTiny
IconStar
IconStar
IconStarSmall
IconStarTiny
IconTag
IconTag
IconTagSmall
IconTagTiny
IconText
IconText
IconTextSmall
IconTextTiny
IconToc
IconToc
IconTocSmall
IconTocTiny
IconTrash
IconTrash
IconTrashSmall
IconTrashTiny
IconTrashOpen
IconTrashOpen
IconTrashOpenSmall
IconTrashOpenTiny
IconTumblr
IconTumblr
IconTwitter
IconTwitter
IconTwitterSmall
IconTwitterTiny
IconUnlock
IconUnlock
IconUnlockSmall
IconUnlockTiny
IconUpvote
IconUpvote
IconUpvoteSmall
IconUpvoteTiny
IconUser
IconUser
IconUserSmall
IconUserTiny
IconUsers
IconUsers
IconUsersSmall
IconUsersTiny
IconVideo
IconVideo
IconVideoSmall
IconVideoTiny
IconVkontakte
IconVkontakte
IconWatch
IconWatch
IconWatchSmall
IconWatchTiny
IconWeibo
IconWeibo
IconWykop
IconWykop
IconYoutube
IconYoutube
IconZoomIn
IconZoomIn
IconZoomInSmall
IconZoomInTiny
IconZoomOut
IconZoomOut
IconZoomOutSmall
IconZoomOutTiny

Logos, avatars and other assets

import AvatarBadgeAdmin from '@wikia/react-common/source/assets/AvatarBadgeAdmin';
import AvatarBadgeContentModerator from '@wikia/react-common/source/assets/AvatarBadgeContentModerator';
import AvatarBadgeDiscussionModerator from '@wikia/react-common/source/assets/AvatarBadgeDiscussionModerator';
import AvatarBadgeGlobalDiscussionsModerator from '@wikia/react-common/source/assets/AvatarBadgeGlobalDiscussionsModerator';
import AvatarBadgeHelper from '@wikia/react-common/source/assets/AvatarBadgeHelper';
import AvatarBadgeStaff from '@wikia/react-common/source/assets/AvatarBadgeStaff';
import AvatarBadgeVstf from '@wikia/react-common/source/assets/AvatarBadgeVstf';
import LogoFandom from '@wikia/react-common/source/assets/LogoFandom';
import LogoFandomHeart from '@wikia/react-common/source/assets/LogoFandomHeart';
import LogoFandomWhite from '@wikia/react-common/source/assets/LogoFandomWhite';
import LogoGamepedia from '@wikia/react-common/source/assets/LogoGamepedia';
import LogoGamepediaBook from '@wikia/react-common/source/assets/LogoGamepediaBook';
import LogoWikia from '@wikia/react-common/source/assets/LogoWikia';
import LogoWikiaOrg from '@wikia/react-common/source/assets/LogoWikiaOrg';
import LogoWikiaOrgWhite from '@wikia/react-common/source/assets/LogoWikiaOrgWhite';
import StoreAppstore from '@wikia/react-common/source/assets/StoreAppstore';
import StoreGoogleplay from '@wikia/react-common/source/assets/StoreGoogleplay';
import StoreLogoDdb from '@wikia/react-common/source/assets/StoreLogoDdb';
import StoreLogoFandom from '@wikia/react-common/source/assets/StoreLogoFandom';

Complete system that can be used in React apps

This is system that controlls the state of site notifications, allows to add them via actions, and outputs them to a component.

It supports plaintext messages and automatically handles dismissing actions when the ⨯ icon is clicked on the notification.

Requirements

The BannerNotification system requires:

  • redux (and react-redux)
  • immutable

Installation

There are few parts that need to be plugged into place:

Reducer

The reducer/store need to be included in Redux.

Example:

import { BannerNotificationsStore } from '@wikia/react-common/systems/BannerNotifications';
import { combineReducers } from 'redux';

const rootReducer = combineReducers({
    BannerNotificationsStore,
});

export default rootReducer;

Component

The component that connects to the Redux state needs to be included on the page somewhere so the notifications are visible.

import React from 'react';
import PropTypes from 'prop-types';
import FandomContentWell from '@wikia/react-common/components/FandomContentWell';
import { BannerNotificationsComponent } from '@wikia/react-common/systems/BannerNotifications';

class PageLayout extends React.Component {
    static propTypes = {
        children: PropTypes.node.isRequired,
    }

    render() {
        return (
            <FandomContentWell>
                <BannerNotificationsComponent />
                {children}
            </FandomContentWell>
        );
    }
}

export default PageLayout;

NOTE: Remember to also include styles!

@import "~@wikia/react-common/systems/BannerNotifications.css";

API

BannerNotifications system exports few actions that should be used to add new notifications:

  • addAlert(text, [id])
  • addWarning(text, [id])
  • addSuccess(text, [id])
  • addMessage(text, [id])

The id is optional and it's needed only when the notification needs to be manually removed with removeNotfication(id) action. If omitted its value is assigned automatically.

Types of notifications are shown in components/BannerNotification section.

Example usage in thunk:

import axios from 'axios';
import { addAlert } from '@wikia/react-common/systems/BannerNotifications';

export const loadData = () => (
    async dispatch => {
        try {
            const response = await axios({
                method: 'get',
                url: `/api/get-data`,
            });
            //...
        } catch (error) {
            dispatch(addAlert('Error loading data from the server'));
            console.error(error);
        }
    }
);

React components

import StyledArticleTagsSelector from '@wikia/react-common/source/components/StyledArticleTagsSelector';

StyledArticleTagsSelector component.

NOTE: Several props are arrays of Tags - Tag is defined as follows:

interface Tag {
  articleId: string;
  articleTitle: string;
  url?: string;
}

Non-empty state:

Tag Wiki Pages
Suggested

Empty state:

Tag Wiki Pages

Too many tags state:

Tag Wiki Pages
Maximum 1 tag
import Avatar from '@wikia/react-common/source/components/Avatar';

Defaults:

Avatar with badges

import StyledAvatar from '@wikia/react-common/source/components/StyledAvatar';

A styled-component version of Avatar component

Defaults:

User avatar

Different sizes:

With hover effect:

Avatar with badges

import AvatarStack from '@wikia/react-common/source/components/AvatarStack';

Defaults:

Image
Image
Image
0

With maxStackSize

Image
Image
+1

With hidden overflow

Image
Image

With overridden counter

Image
Image
+4
import BannerNotification from '@wikia/react-common/source/components/BannerNotification';

This is a single component used in BannerNotifications component.

By default its rendered without close button:

lorem ipsum - messge

But it can be rendered with close buttton:

lorem ipsum - alert

Or with extra HTML:

This is a text with a link
import BannerNotifications from '@wikia/react-common/source/components/BannerNotifications';

Component used to create notifications. For full functionality it needs some app logic to handle the array of messages - adding/removing.

See the following:

The messages prop is an array of bannerNotificationsMessageType objects with the following props:

  • id: unique string that's send as the param of the onClose function
  • type: one of: 'alert', 'warning', 'success' or 'message'.
  • text: text that is going to be displayed on the notification
  • permanent: a boolean flag - if present the close button won't be displayed on the notification

bannerNotificationsMessageType is exported along with BannerNotification

By default it renders nothing:

But with proper data it can display all the messages:

this is a permanent message
this is a success
this is a warning
this is an alert
import Button from '@wikia/react-common/source/components/Button';

Basic button component

Defaults:

Different styles:

Link

Full width:

import ButtonGroup from '@wikia/react-common/source/components/ButtonGroup';

Button group component

Regular buttons:

Secondary buttons:

import Checkbox from '@wikia/react-common/source/components/Checkbox';

Defaults:

Disabled checkbox:

import Radio from '@wikia/react-common/source/components/Radio';

Each radio must have a value assigned to it.

Defaults:

Disabled:

import RadioGroup from '@wikia/react-common/source/components/RadioGroup';

Defaults:

Cats or dogs?
import ContentWell from '@wikia/react-common/source/components/ContentWell';

ContentWell wraps children in wds-content-well CSS mixin.

Open this example in the new page to see the difference:

ContentWell
import Countdown from '@wikia/react-common/source/components/Countdown';

Simple circular, countdown-from-10 component with callback.

Click the theme buttons to re-initialize examples.

10

This timer is always stopped:

10
import StyledCountdown from '@wikia/react-common/source/components/StyledCountdown';

Simple circular, countdown-from-10 component with callback.

Click the theme buttons to re-initialize examples.

10

This timer is always stopped:

10
import StyledDropdown from '@wikia/react-common/source/components/StyledDropdown';

Defaults:

Dropdown using dropdown-tiny chevron:

Nested dropdown:

import ExpandableText from '@wikia/react-common/source/components/ExpandableText';

ExpandableText component can be used to temporarily limit text showed to the user. It has a button used to fully expand the text. If the source text is shorter than the limit the button do not show.

Both button label and string used to ellipsis has to be configured. Button and the text itself can be syled with classes passed to the component.

Defaults:

Lorem ipsum dolor am… 
import FandomBackgroundImage from '@wikia/react-common/source/components/FandomBackgroundImage';

An image background that can be used as a sample image.

Defaults:

Custom width and className:

import FandomContentWell from '@wikia/react-common/source/components/FandomContentWell';

FandomContentWell wraps children in wds-content-well($use-xxlarge-breakpoint: false) CSS mixin.

Open this example in the new page to see the difference:

FandomContentWell
import Fieldset from '@wikia/react-common/source/components/Fieldset';

Defaults:

You need a valid e-mail for the service
import FloatingButton from '@wikia/react-common/source/components/FloatingButton';

Floating button (icons-only)

Defaults:

import FloatingButtonGroup from '@wikia/react-common/source/components/FloatingButtonGroup';

Floating button group

Defaults:

Vertical:

import HideComponent from '@wikia/react-common/source/components/HideComponent';

Component that's used to either show or hide own children.

Since both show and hide shouldn't be used at the same time, hide has a priority

hideshowComponent visible?
undefinedundefinedyes
undefinedtrueyes
undefinedfalseno
true(any value)no
false(any value)yes

Hide:

Display

Default
import HighlightedText from '@wikia/react-common/source/components/HighlightedText';

HighlightedText is a text node with highlighted text.

One match:

Invisibility

Multiple matches:

Invisibilitinv

No matches:

Invisibility
import Icon from '@wikia/react-common/source/components/Icon';

A single WDS icon.

NOTE: This icon is using IconSprite component.

This component uses the Design System icons listed at https://fandomdesignsystem.com/#/components/assets

The name attribute of the component refers to the icon's DS name.

NOTICE: This component requires <IconSprite /> to work.

Example usage

Standard icon:

Small or tiny icon:

Add your own CSS class for styling:

Other props are just passed to the SVG:

import IconSprite from '@wikia/react-common/source/components/IconSprite';

This component needs to be included once in the app in order to use <Icon> component if there's no other way that the icons are included.

import Image from '@wikia/react-common/source/components/Image';

Default:

test

Disable lazy loading

test
import ImagePreloader from '@wikia/react-common/source/components/ImagePreloader';

A helper component that encapsulates image preloading logic.

ImagePreloader component can be used to load (or preload) any image. Both onChange and children are functions that are called with ImagePreloader's state with the following:

  • state
    • the current state of the preloading - can be either pending, success or error All the states are xported via ImagePreloader.STATE const.
  • error
    • a JavaScript Error object (if the state is error) or null
  • src
    • taken from ImagePreloader's props
  • srcSet
    • taken from ImagePreloader's props

Usage:

import ImagePreloader from '@wikia/react-common/components/ImagePreloader';

export default ImageWithLoadingMessage = () => (
    <ImagePreloader src="http://path.to.image.jpg">
        {({ state, src }) => {
            if (state === ImagePreloader.STATE.PENDING) {
                return <span>Loading image...</span>;
            }

            if (state === ImagePreloader.STATE.ERROR) {
                return <span>Error loading image</span>;
            }

            return <img src={src} />;
        }}
    </ImagePreloader>
);
import Input from '@wikia/react-common/source/components/Input';

An empty input:

An empty email input with hint:

Type your e-mail

Other available types are: text (default), number, email, search, tel, url and password.

An empty email input with hint (node):

STRONG hint

A non-empty input:

Disabled input:

Readonly input:

Input with placeholder:

A non-empty input with the error state:

Need something else

An non-empty textarea:

An resizeable textarea:

An autoresizeable textarea:

An autoresizeable textarea with custom initial number of rows:

import List from '@wikia/react-common/source/components/List';

Default:

  • First one
  • Second one
  • Third one

A list with "big items". This makes padding borader and the font size bigger in each element

  • First one
  • Second one
  • Third one

A list with bolded elements.

  • First one
  • Second one
  • Third one

A list with a separator line between each element.

  • First one
  • Second one
  • Third one

A list with links. This is necessary to ensure the elements are fully clickable and are without any surrounding gaps.

A list with ellipsis.

  • First one...
  • Second one...
  • Third one...
import StyledList from '@wikia/react-common/source/components/StyledList';

Default:

  • First one
  • Second one
  • Third one

A list with "big items". This makes padding borader and the font size bigger in each element

  • First one
  • Second one
  • Third one

A list with bolded elements.

  • First one
  • Second one
  • Third one

A list with a separator line between each element.

  • First one
  • Second one
  • Third one

A list with links. This is necessary to ensure the elements are fully clickable and are without any surrounding gaps.

A list with ellipsis.

  • First one...
  • Second one...
  • Third one...
import Portal from '@wikia/react-common/source/components/Portal';

Open the source and search for "Portal demo".

<Portal id="asd">
  <p>This Portal demo will be rendered in Body</p>
</Portal>
import Select from '@wikia/react-common/source/components/Select';

A select input. For post-interaction events (onBlur, onChange) the signature is onX(value, label) for single-value selects, and onX([{value, label}, ...]) for multi-select.

Use Select.createOption to create options to feed to the options prop.

Basic usage:

Select...

Grouped options:

Select...

Non-searchable:

Select...

Loading indicator:

Loading...

Multi-select

Select...

Controlled multi-input

label 1
label 2
label 5

Controlled single-input

label 1
import SimpleLocalNavigation from '@wikia/react-common/source/components/SimpleLocalNavigation';

SimpleLocalNavigation is a simple navigation than can be used with links or react-router.

To mark child as active use exported static SimpleLocalNavigation.ACTIVE_CLASS_NAME.

Open this example in the new page to see the difference:

FooBarThird option
import Spinner from '@wikia/react-common/source/components/Spinner';

Loader block component used to indicate loading state.

Based on http://fandomdesignsystem.com/#/components/progress-indicators

Defaults:

Custom size and stroke:

import Switch from '@wikia/react-common/source/components/Switch';
import Switch.Item from '@wikia/react-common/source/components/Switch.Item';

NOTE: This component should only be used with <Switch.Wrapper>, see <Switch> above for a complete example.

import Switch.Wrapper from '@wikia/react-common/source/components/Switch.Wrapper';

NOTE: This component should only be used with <Switch.Item>, see <Switch> above for a complete example.

Empty wrapper:

import StyledTag from '@wikia/react-common/source/components/StyledTag';

StyledTag component. Used mostly as an ArticleTag

Defaults:

This is a default tag

With extra options

This is a secondary/removable tag
import Timeago from '@wikia/react-common/source/components/Timeago';

The Timeago component is a small component that shows the number of seconds/minutes/days from given datetime.

One second ago:

Few minutes ago:

Few days ago:

import VideoPlayIcon from '@wikia/react-common/source/components/VideoPlayIcon';

Video Play icon

Defaults:

Custom size:

import StyledErrorDisplayingContent from '@wikia/react-common/source/components/StyledErrorDisplayingContent';

A standard error message when content cannot be displayed.

Error message:

Sorry, we’re having trouble displaying this content right now.

Adds a hide prop logic to newly created component

Usage

import withHideComponent from '@react-common/hocs/withHideComponent';

const Comp = props => (<div> test </div>)
const ComponentWithIsHidden = withTimeoutFallback(Spinner);

// Usage
<ComponentWithIsHidden hide/> // ComponentWithIsHidden will be hidden
<ComponentWithIsHidden /> // ComponentWithIsHidden will be visible

Adds a component to display after a set time (accurate to the nearest 100ms)

Usage

import withTimeoutFallback from '@react-common/hocs/withTimeoutFallback';

const Spinner = props => (<div> ...spinner </div>)
const SpinnerWithTimeout = withTimeoutFallback(Spinner);

// Usage
<SpinnerWithTimeout />

Custom Fallback and Timeout:

import withTimeoutFallback from '@react-common/hocs/withTimeoutFallback';

const Spinner = props => (<div> ...spinner </div>)
const Fallback = props => (<div> Error Loading </div>)
const options = {timeout: 10000, FallbackComponent: Fallback};
const SpinnerWithTimeout = withTimeoutFallback(Spinner, options);

// Usage
<SpinnerWithTimeout />

Add an error boundary to any component. By default every error will be sent to the remote log.

withErrorBoundary(Component, options);

The options param is optional and can have the following:

  • options.name
    • Boundary's name; this will be used in console.log and will be sent to remote log NOTE: By default it's equal to Component.name (class name of the Component HoC param)
  • options.fallback
    • a component that will be displayed if there's an error
  • options.skipLog
    • if true the remote log will not be used; useful for developing/debugging local code
  • options.appName
    • Front-End application's name; this is sent to remote log
  • options.appVersion
    • Front-End application's verion; this is sent to remote log

There's handy component for fallback - StyledErrorDisplayingContent;

Examples

Very basic usage:

import withErrorBoundary from '@react-common/hocs/withErrorBoundary';

const Component = props => (<div> Test </div>);
const ComponentWithErrorBoundary = withErrorBoundary(Component);

// Usage
<ComponentWithErrorBoundary />

Options:

import withErrorBoundary from '@react-common/hocs/withErrorBoundary';

const Component = props => (<div> Test </div>);
const FallbackComponent = () => (<div> Alternate error message </div>);
const ComponentWithErrorBoundary = withErrorBoundary(Component, {
    name: 'BoundaryName',
    fallback: FallbackComponent,
});

// Usage
<ComponentWithErrorBoundary />

Only render a component on the browser

Usage

import withDisabledSSR from '@react-common/hocs/withDisabledSSR';

const Component = props => (<div> ...spinner </div>)
const ComponentWithDisabledSSR = withDisabledSSR(Component);

// Usage
<ComponentWithDisabledSSR />

Custom hook that can be used for a periodical event.

The first param is the callback, the 2nd param is the delay between calls - null can be used to pause.

Usage

import React, { useState } from 'react';
import useInterval from '@react-common/hooks/useInterval';

// Usage
const CounterComponent = () => {
    const [value, setValue] = useState(0);

    useInterval(() => {
        setValue(value + 1);
    }, 1000);

    return (
        <div>
            Counter: <span>{value}</span>
        </div>
    );
};

Custom hook that calls given handler when element will be scrolled near (100px) it's bottom.

Returns an element to be used as a ref for scrollable element.

Usage

import useLazyLoad from '@react-common/hooks/useLazyLoad';

// Usage
const Component = ({ handler }) => {
    const ref = useLazyLoad(handler);

    return (
        <div ref={ref}>
            ...
        </div>
    );
};

Hook to create a React Portal.

Automatically handles creating and tearing-down the root elements (no SRR makes this trivial), so there is no need to ensure the parent target already exists.

Usage

import usePortal from '@react-common/hooks/usePortal';

function Component({ id, children }) {
    const target = usePortal(id, [id]);
    return createPortal(children, target);
}

The utils/environment.js offers a few environment config helpers including detection of browser and service urls based on the env.

Usage:

import { isBrowser, isFandomCom, getServicesBaseURL, isProduction, isSandbox, getEventLoggerBase } from '@wikia/react-common/utils/environment';

isProduction();         // checks if the app was built with the production flag (might not be in a prod env though)
isBrowser();            // checks if the window global is available
isFandomCom();          // used to determine the end points for services
isSandbox();            // checks if url is a sandbox

getServicesBaseURL();   // gives the correct services url base on the environment
getEventLoggerBase();   // correct event logger url

The utils/vignette.js offers few utilities that can be used to deal with Vignette images - most importantly the validation and manipulation of Vignette's URLs.

Validation

There are two functions can be used to validate a proper Vignette ID and URL:

  • isVignetteUrl(url: String): Boolean
  • isVignetteId(id: String): Boolean
  • vignette(imageUrl): VignetteHelper

VignetteHelper class

This class can be used to extract and manipulate URLs - it can extract Vignette params from given image and new options can be applied. Class' instance keeps the base image URL (server + ID) as well as all the Vignette params.

NOTE: The vignette(url) function can be used instead of new VignetteHelper(url).

API

All the following are instance methods:

  • isOk(): Boolean
    • returns true if the image has been correctly set
  • resetParams(): void
    • resets Vignette params
  • set(url: String): Boolean
    • extracts base image URL and params from Vignette URL; returns false if the URL is not a Vignette one
  • getParams(): String
    • returns the Vigentte params
  • get(): String
    • returns full Vignette URL
  • clone(): VignetteHelper
    • returns clone of the instance
  • withScaleToHeight(height: Number, allowUpscaling: Boolean = false): void
    • returns new instance with changed Vignette params
  • withScaleToWidth(width: Number, allowUpscaling: Boolean = false): void
    • returns new instance with changed Vignette params
  • withThumbnail(width: Number, height: Number, allowUpscaling: Boolean = false): void
    • returns new instance with changed Vignette params
  • withTopCrop(width: Number, height: Number): void
    • returns new instance with changed Vignette params
  • withSmart(width: Number, height: Number): void
    • returns new instance with changed Vignette params
  • withAuto(width: Number, height: Number, allowUpscaling: Boolean = false): void
    • returns new instance with changed Vignette params
  • withTransform(): VignetteHelper
    • returns clone of the instance with changed Vignette params

Usage:

import { VignetteHelper } from '@wikia/react-common/utils/vignette';

const ThumbImage = ({ alt, src, width, height }) => {
    const image = new VignetteHelper(src);
    const image300px = image.withScaleToWidth(300).get();

    const srcSet = `
        ${image300px} 300w
        ${image.withScaleToWidth(600).get()} 600w
        ${image.withScaleToWidth(1000).get()} 2x
    `;

    return <img src={image300px} alt={alt} srcSet={srcSet} />;
}

// this will be the same as

import { VignetteHelper } from '@wikia/react-common/utils/vignette';

const ThumbImage = ({ alt, src, width, height }) => {
    const standardImage = vignette(src);

    const srcSet = `
        ${standardImage.withScaleToWidth(300).get()} 300w
        ${standardImage.withScaleToWidth(600).get()} 600w
        ${standardImage.withScaleToWidth(1000).get()} 2x
    `;

    return <img src={standardImage.get()} alt={alt} srcSet={srcSet} />;
}

More

Additionally, there are few other exports that are used internally, but might be useful:

  • VIGNETTE_DEFAULT_SERVER
  • VIGNETTE_MODES
  • VIGNETTE_UUID_REGEX
  • VIGNETTE_SERVER_REGEX
  • VIGNETTE_BASE_IMAGE_REGEX

The utils/copyToClipboard.js utility can copy any string to the users clipboard if it coincides with a click.

Usage:

import copyToClipboard from '@wikia/react-common/utils/copyToClipboard';

function Component() {
    return <div onClick={() => copyToClipboard('string-to-copy')} /> 
}

The utils/uuidv4.js generates a UUID v4 on each call

Usage:

import uuidv4 from '@wikia/react-common/utils/uuidv4';

uuidv4();         // generates UUID v4

Wrappers for immutable.js Records.

Model is a simple wrapper on top of immutable.js' Record in order to simplify Record's usage.

Usage

import Model from '@react-common/models/Model';

export const MY_MODEL_KEYS = Object.freeze({
    type: 'type',
    name: 'name',
});

const myModelSchema = {
    [MY_MODEL_KEYS.type]: '',
    [MY_MODEL_KEYS.name]: '',
};

export default class MyModel extends Model(myModelSchema, 'MyModel') {
    // ...
}

Model provides few APIs:

Model.empty()

This always returns an empty instance of model (with fields set to what's defined in the schema). It's most useful when generating Redux reducers.

export default function MyStoreReducer(state = MyModel.empty(), action = {}) {
    const { type } = action;

    switch (type) {
        // ...
        default:
            return state;
    }
}

Model.build(...)

This builds Model from POJO (or Model instance) param. If the param is anything but Object or Model it will return undefined instead.

const data = {
    name: 'foo',
    type: 'bar',
};

const instance = MyStore.build(data);

Model.buildList(...)

This builds a immutable.js List of Models from an array of POJO.

const data = [
    {
        name: 'foo',
        type: 'bar',
    },
    {
        name: 'another one',
        type: 'another one too'
    }
];

const list = MyStore.buildList(data);

Loadable is a simple helper Model that's intended to keep a loading state and provide an API similar to Finite-state Machine.

The Loadable's state can be in one of four different states:

  • Uninitialized (the default, initial state)
  • In Progress
  • Completed
  • Error

Typical usage

Usually the Loadable will start as uninitialized, after that it will get switched to In Progress and either Completed or Error state.

  • (successful loading) Uninitialized -> In Progress -> Completed
  • (unsuccessful loading) Uninitialized -> In Progress -> Error

APIs for reading state

  • Loadable.isLoadingUninitialized()
    • returns true is the current state is Uninitialized
  • Loadable.isLoadingInProgress()
    • returns true is the current state is In Progress
  • Loadable.isLoadingCompleted()
    • returns true is the current state is Completed
  • Loadable.isLoadingError()
    • returns true is the current state is Error
  • Loadable.isLoadingFinished()
    • returns true is the current state is either Completed or Error (useful in checking if any loading action has been finished)

APIs for writing the state

  • Loadable.withLoadingInProgress()
    • returns new instance with state set to In Progress
  • Loadable.withLoadingCompleted()
    • returns new instance with state set to Completed
  • Loadable.withLoadingError()
    • returns new instance with state set to Error