Skip to content

Commit

Permalink
feat(uploader): add react component
Browse files Browse the repository at this point in the history
  • Loading branch information
gcornut committed Nov 5, 2019
1 parent fb8d25f commit ecf4d50
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.react.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
-   Multiple Autocomplete component [#209](https://github.com/lumapps/design-system/pull/209)
-   `isHighlighted` prop for `Chip` component [#209](https://github.com/lumapps/design-system/pull/209)
-   Added `isClearable` and `chips` props for Autocomplete [#209](https://github.com/lumapps/design-system/pull/209)
- Uploader component [#208](https://github.com/lumapps/design-system/pull/208)

## [0.13.0][] - 2019-11-04

Expand Down
48 changes: 48 additions & 0 deletions demo/react/doc/product/components/uploader.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
```javascript import
import { Uploader, UploaderVariant } from 'LumX';
import { mdiImagePlus } from 'LumX/icons';
```

# Uploader

## Default

```javascript jsx withThemeSwitcher
(theme) => (
<Uploader
icon={mdiImagePlus}
label="Add profile picture"
theme={theme}
/>
);
```

## Rounded

```javascript jsx withThemeSwitcher
(theme) => (
<Uploader
icon={mdiImagePlus}
label="Add profile picture"
theme={theme}
variant={UploaderVariant.rounded}
/>
);
```

## Circle

```javascript jsx withThemeSwitcher
(theme) => (
<Uploader
icon={mdiImagePlus}
label="Add profile picture"
theme={theme}
variant={UploaderVariant.circle}
/>
);
```

### Properties

<PropTable component="Uploader" />
1 change: 1 addition & 0 deletions demo/react/layout/MainNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const ITEMS: Item[] = [
'Thumbnail',
'Toolbar',
'Tooltip',
'Uploader',
'User block',
],
},
Expand Down
120 changes: 120 additions & 0 deletions src/components/uploader/react/Uploader.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React, { ReactElement } from 'react';

import { mount, shallow } from 'enzyme';

import { ICommonSetup, Wrapper, commonTestsSuite } from 'LumX/core/testing/utils.test';
import { getBasicClass } from 'LumX/core/utils';

import { CLASSNAME, DEFAULT_PROPS, Uploader, UploaderProps } from './Uploader';

/////////////////////////////

/**
* Define the overriding properties waited by the `setup` function.
*/
type ISetupProps = Partial<UploaderProps>;

/**
* Defines what the `setup` function will return.
*/
interface ISetup extends ICommonSetup {
props: ISetupProps;

/**
* [Enter the description of this wrapper].
* [You should also probably change the name of the wrapper to something more meaningful].
*/
wrapper: Wrapper;
}

/////////////////////////////

/**
* Mounts the component and returns common DOM elements / data needed in multiple tests further down.
*
* @param props The props to use to override the default props of the component.
* @param [shallowRendering=true] Indicates if we want to do a shallow or a full rendering.
* @return An object with the props, the component wrapper and some shortcut to some element inside of the component.
*/
const setup = (props: ISetupProps = {}, shallowRendering: boolean = true): ISetup => {
const renderer: (el: ReactElement) => Wrapper = shallowRendering ? shallow : mount;

// @ts-ignore
const wrapper: Wrapper = renderer(<Uploader {...props} />);

return {
props,
wrapper,
};
};

describe(`<${Uploader.displayName}>`, (): void => {
// 1. Test render via snapshot (default states of component).
describe('Snapshots and structure', (): void => {
// Here is an example of a basic rendering check, with snapshot.

it('should render correctly', (): void => {
const { wrapper } = setup();
expect(wrapper).toMatchSnapshot();

expect(wrapper).toExist();
expect(wrapper).toHaveClassName(CLASSNAME);
});
});

/////////////////////////////

// 2. Test defaultProps value and important props custom values.
describe('Props', (): void => {
// Here are some examples of basic props check.

it('should use default props', (): void => {
const { wrapper } = setup();

Object.keys(DEFAULT_PROPS).forEach((prop: string): void => {
expect(wrapper).toHaveClassName(
getBasicClass({ prefix: CLASSNAME, type: prop, value: DEFAULT_PROPS[prop] }),
);
});
});
});

/////////////////////////////

// 3. Test events.
describe('Events', (): void => {
// Here is an example how to check a `onClick` event.

const onClick: jest.Mock = jest.fn();

beforeEach((): void => {
onClick.mockClear();
});

it('should trigger `onClick` when clicked', (): void => {
const { wrapper } = setup({ onClick }, false);

wrapper.simulate('click');

expect(onClick).toHaveBeenCalled();
});
});
/////////////////////////////

// 4. Test conditions (i.e. things that display or not in the UI based on props).
describe('Conditions', (): void => {
// Nothing to do here.
});

/////////////////////////////

// 5. Test state.
describe('State', (): void => {
// Nothing to do here.
});

/////////////////////////////

// Common tests suite.
commonTestsSuite(setup, { className: 'wrapper', prop: 'wrapper' }, { className: CLASSNAME });
});
137 changes: 137 additions & 0 deletions src/components/uploader/react/Uploader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React, { MouseEventHandler, ReactElement } from 'react';

import classNames from 'classnames';

import { AspectRatio, Icon, Size, Theme } from 'LumX';
import { COMPONENT_PREFIX } from 'LumX/core/react/constants';
import { handleBasicClasses } from 'LumX/core/utils';
import { IGenericProps, getRootClassName } from 'LumX/react/utils';

/////////////////////////////

enum UploaderVariant {
square = 'square',
rounded = 'rounded',
circle = 'circle',
}

type UploaderSize = Size.xl | Size.xxl;

/**
* Defines the props of the component.
*/
interface IUploaderProps extends IGenericProps {
/**
* Aspect ratio
*/
aspectRatio?: AspectRatio;
/**
* Icon
*/
icon?: string;
/**
* Label
*/
label?: string;
/**
* Size
*/
size?: UploaderSize;
/**
* Theme
*/
theme?: Theme;
/**
* Uploader variant
*/
variant?: UploaderVariant;
/**
* On click handler
*/
onClick?: MouseEventHandler<HTMLDivElement>;
}
type UploaderProps = IUploaderProps;

/////////////////////////////

/////////////////////////////
// //
// Public attributes //
// //
/////////////////////////////

/**
* The display name of the component.
*/
const COMPONENT_NAME = `${COMPONENT_PREFIX}Uploader`;

/**
* The default class name and classes prefix for this component.
*/
const CLASSNAME = getRootClassName(COMPONENT_NAME);

/**
* The default value of props.
*/
const DEFAULT_PROPS: Partial<UploaderProps> = {
aspectRatio: AspectRatio.horizontal,
size: Size.xl,
theme: Theme.light,
variant: UploaderVariant.square,
};

/////////////////////////////

/**
* [Enter the description of the component here].
*
* @return The component.
*/
const Uploader: React.FC<UploaderProps> = (props: UploaderProps): ReactElement => {
const {
aspectRatio = DEFAULT_PROPS.aspectRatio,
className,
label,
icon,
size = DEFAULT_PROPS.size,
theme = DEFAULT_PROPS.theme,
variant = DEFAULT_PROPS.variant,
...forwardedProps
} = props;

// Square aspect ratio for circle variants.
const adjustedAspectRatio = variant === UploaderVariant.circle ? AspectRatio.square : aspectRatio;

return (
<div
className={classNames(
className,
handleBasicClasses({
aspectRatio: adjustedAspectRatio,
prefix: CLASSNAME,
size,
theme,
variant,
}),
)}
{...forwardedProps}
>
<div className={`${CLASSNAME}__background`} />

<div className={`${CLASSNAME}__wrapper`}>
{icon && (
<div className={`${CLASSNAME}__icon`}>
<Icon icon={icon} size={Size.s} />
</div>
)}

{label && <span className={`${CLASSNAME}__label`}>{label}</span>}
</div>
</div>
);
};
Uploader.displayName = COMPONENT_NAME;

/////////////////////////////

export { CLASSNAME, DEFAULT_PROPS, Uploader, UploaderProps, UploaderVariant };
14 changes: 14 additions & 0 deletions src/components/uploader/react/__snapshots__/Uploader.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<Uploader> Snapshots and structure should render correctly 1`] = `
<div
className="lumx-uploader lumx-uploader--aspect-ratio-horizontal lumx-uploader--size-xl lumx-uploader--theme-light lumx-uploader--variant-square"
>
<div
className="lumx-uploader__background"
/>
<div
className="lumx-uploader__wrapper"
/>
</div>
`;
2 changes: 2 additions & 0 deletions src/react.index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,5 @@ export { SideNavigationItem, SideNavigationItemProps } from 'LumX/components/sid
export { Slider } from 'LumX/components/slider/react/Slider';

export { Dialog, DialogSizes } from 'LumX/components/dialog/react/Dialog';

export { Uploader, UploaderProps, UploaderVariant } from 'LumX/components/uploader/react/Uploader';

0 comments on commit ecf4d50

Please sign in to comment.