Remove Hero, HeroSection and heroBanners reducer (#10170)
* Remove Hero, HeroSection and heroBanners reducer * Remove unused dependency
This commit is contained in:
Родитель
78d2da91b4
Коммит
3bf0f80f87
|
@ -178,7 +178,6 @@
|
|||
"jed": "1.1.1",
|
||||
"join-url": "2.0.0",
|
||||
"jsdom": "16.5.0",
|
||||
"knuth-shuffle": "1.0.8",
|
||||
"localforage": "1.9.0",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"moment": "2.29.1",
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
/* @flow */
|
||||
import makeClassName from 'classnames';
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { compose } from 'redux';
|
||||
|
||||
import { setHeroBannerOrder } from 'amo/reducers/heroBanners';
|
||||
import Card from 'amo/components/Card';
|
||||
import HeroSection from 'amo/components/HeroSection';
|
||||
import type { AppState } from 'amo/store';
|
||||
import type { DispatchFunc } from 'amo/types/redux';
|
||||
|
||||
import './styles.scss';
|
||||
|
||||
export type HeroSectionsType = Array<React.Element<typeof HeroSection>>;
|
||||
|
||||
type Props = {|
|
||||
name: string,
|
||||
random?: boolean,
|
||||
sections: HeroSectionsType,
|
||||
|};
|
||||
|
||||
type InternalProps = {|
|
||||
...Props,
|
||||
dispatch: DispatchFunc,
|
||||
heroBanners: Object,
|
||||
|};
|
||||
|
||||
export class HeroBase extends React.Component<InternalProps> {
|
||||
constructor(props: InternalProps) {
|
||||
super(props);
|
||||
|
||||
const { dispatch, heroBanners, name, random, sections } = props;
|
||||
|
||||
if (!heroBanners[name]) {
|
||||
dispatch(setHeroBannerOrder({ name, random, sections }));
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { heroBanners, name, sections } = this.props;
|
||||
const orderStyle = heroBanners[name]
|
||||
? `Hero-order-${heroBanners[name].order.join('-')}`
|
||||
: null;
|
||||
|
||||
return (
|
||||
<Card className={makeClassName('Hero', `Hero-name-${name}`, orderStyle)}>
|
||||
<div className="Hero-contents">
|
||||
{heroBanners[name]
|
||||
? heroBanners[name].order.map((index) => {
|
||||
return sections[index];
|
||||
})
|
||||
: null}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const mapStateToProps = (state: AppState) => {
|
||||
return { heroBanners: state.heroBanners };
|
||||
};
|
||||
|
||||
const Hero: React.ComponentType<Props> = compose(connect(mapStateToProps))(
|
||||
HeroBase,
|
||||
);
|
||||
|
||||
export default Hero;
|
|
@ -1,17 +0,0 @@
|
|||
@import '~amo/css/styles';
|
||||
|
||||
.Hero {
|
||||
margin: 0 0 $padding-page;
|
||||
|
||||
@include respond-to(large) {
|
||||
margin: 0 0 $padding-page-l;
|
||||
}
|
||||
}
|
||||
|
||||
.Hero .Card-contents {
|
||||
padding: 12px;
|
||||
|
||||
@include respond-to(large) {
|
||||
padding: $padding-page-l;
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/* @flow */
|
||||
import makeClassName from 'classnames';
|
||||
import * as React from 'react';
|
||||
|
||||
import Link from 'amo/components/Link';
|
||||
|
||||
import './styles.scss';
|
||||
|
||||
type Props = {|
|
||||
children?: any,
|
||||
linkTo?: Object | string,
|
||||
onClick?: () => void,
|
||||
styleName?: string,
|
||||
|};
|
||||
|
||||
export default class HeroSection extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
styleName: 'default',
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children, linkTo, styleName } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={makeClassName(
|
||||
'HeroSection',
|
||||
`HeroSection-styleName--${String(styleName)}`,
|
||||
)}
|
||||
>
|
||||
{linkTo ? (
|
||||
<Link
|
||||
className="HeroSection-link-wrapper"
|
||||
onClick={this.props.onClick}
|
||||
to={linkTo}
|
||||
>
|
||||
<div className="HeroSection-content">{children}</div>
|
||||
</Link>
|
||||
) : (
|
||||
<div className="HeroSection-wrapper">
|
||||
<div className="HeroSection-content">{children}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
@import '~amo/css/styles';
|
||||
|
||||
.HeroSection {
|
||||
border-radius: $border-radius-default;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.HeroSection-wrapper,
|
||||
.HeroSection-link-wrapper,
|
||||
.HeroSection-link-wrapper:link {
|
||||
align-items: flex-end;
|
||||
color: $white;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
height: 100%;
|
||||
min-height: 128px;
|
||||
padding: $padding-page;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
|
||||
@include respond-to(large) {
|
||||
padding: $padding-page-l;
|
||||
}
|
||||
}
|
||||
|
||||
.HeroSection-link-wrapper:focus {
|
||||
.HeroSection-content {
|
||||
outline: 1px dotted $white;
|
||||
outline: auto 5px -webkit-focus-ring-color;
|
||||
}
|
||||
}
|
||||
|
||||
.HeroSection-content {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.HeroSection-content h3 {
|
||||
@include font-light();
|
||||
|
||||
font-size: $font-size-m;
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
|
||||
@include respond-to(medium) {
|
||||
font-size: $hero-title-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
.HeroSection-content p {
|
||||
@include font-light();
|
||||
|
||||
display: block;
|
||||
font-size: $font-size-m-smaller;
|
||||
line-height: initial;
|
||||
margin: 10px 0 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
|
||||
@include respond-to(medium) {
|
||||
font-size: $hero-subtitle-font-size;
|
||||
margin: 20px 0 0;
|
||||
}
|
||||
}
|
|
@ -43,10 +43,6 @@ $font-size-hero-heading: 38px;
|
|||
|
||||
$font-size-header-title: 34px;
|
||||
|
||||
// Hero
|
||||
$hero-title-font-size: 33px;
|
||||
$hero-subtitle-font-size: 22px;
|
||||
|
||||
// Animation Variables
|
||||
$transition-short: 150ms;
|
||||
$transition-medium: $transition-short * 3;
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
import { knuthShuffle } from 'knuth-shuffle';
|
||||
|
||||
// Hero banners have three items max, all the time :-)
|
||||
const MAX_ITEMS = 3;
|
||||
export const SET_HERO_BANNER_ORDER = 'SET_HERO_BANNER_ORDER';
|
||||
|
||||
export const setHeroBannerOrder = ({ name, random = false, sections }) => {
|
||||
if (!name) {
|
||||
throw new Error('name is required');
|
||||
}
|
||||
if (!sections) {
|
||||
throw new Error('sections are required');
|
||||
}
|
||||
|
||||
return {
|
||||
payload: { name, random, sections },
|
||||
type: SET_HERO_BANNER_ORDER,
|
||||
};
|
||||
};
|
||||
|
||||
export const initialState = {};
|
||||
|
||||
export default function heroBannerOrderReducer(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case SET_HERO_BANNER_ORDER: {
|
||||
const { name, random, sections } = action.payload;
|
||||
const orderArray = [...sections.keys()];
|
||||
const order = random ? knuthShuffle(orderArray) : orderArray;
|
||||
|
||||
return {
|
||||
...state,
|
||||
[name]: {
|
||||
order: order.slice(0, MAX_ITEMS),
|
||||
},
|
||||
};
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -33,7 +33,6 @@ import categories from 'amo/reducers/categories';
|
|||
import errors from 'amo/reducers/errors';
|
||||
import errorPage from 'amo/reducers/errorPage';
|
||||
import formOverlay from 'amo/reducers/formOverlay';
|
||||
import heroBanners from 'amo/reducers/heroBanners';
|
||||
import languageTools from 'amo/reducers/languageTools';
|
||||
import infoDialog from 'amo/reducers/infoDialog';
|
||||
import installations from 'amo/reducers/installations';
|
||||
|
@ -145,7 +144,6 @@ type InternalAppState = {|
|
|||
errorPage: ErrorPageState,
|
||||
errors: Object,
|
||||
formOverlay: FormOverlayState,
|
||||
heroBanners: Object,
|
||||
home: HomeState,
|
||||
infoDialog: InfoDialogState,
|
||||
installations: InstallationsState,
|
||||
|
@ -203,7 +201,6 @@ export const reducers: AppReducersType = {
|
|||
errors,
|
||||
errorPage,
|
||||
formOverlay,
|
||||
heroBanners,
|
||||
home,
|
||||
infoDialog,
|
||||
installations,
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
import { mount } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import { setHeroBannerOrder } from 'amo/reducers/heroBanners';
|
||||
import Hero from 'amo/components/Hero';
|
||||
import { dispatchClientMetadata } from 'tests/unit/helpers';
|
||||
|
||||
describe(__filename, () => {
|
||||
const defaultProps = {
|
||||
store: dispatchClientMetadata().store,
|
||||
};
|
||||
let dispatchSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
dispatchSpy = sinon.spy(defaultProps.store, 'dispatch');
|
||||
});
|
||||
|
||||
// We mount certain tests to ensure lifecycle methods like componentDidMount
|
||||
// are called.
|
||||
// See: https://github.com/mozilla/addons-frontend/issues/3349
|
||||
function mountRender({ ...props } = {}) {
|
||||
return mount(<Hero {...defaultProps} {...props} />);
|
||||
}
|
||||
|
||||
it('renders a Hero', () => {
|
||||
const name = 'TestingPage';
|
||||
const sections = [
|
||||
<p className="something" key="something">
|
||||
Hello!
|
||||
</p>,
|
||||
<p className="something-else" key="something-else">
|
||||
Bonjour !
|
||||
</p>,
|
||||
];
|
||||
const root = mountRender({ name, random: true, sections });
|
||||
|
||||
sinon.assert.calledWith(
|
||||
dispatchSpy,
|
||||
setHeroBannerOrder({ name, random: true, sections }),
|
||||
);
|
||||
expect(root.find('.Card.Hero')).toHaveLength(1);
|
||||
expect(root.find('.Card.Hero-name-TestingPage')).toHaveLength(1);
|
||||
expect(root.find('.something')).toHaveLength(1);
|
||||
expect(root.find('.something-else')).toHaveLength(1);
|
||||
});
|
||||
});
|
|
@ -1,64 +0,0 @@
|
|||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
|
||||
import Link from 'amo/components/Link';
|
||||
import HeroSection from 'amo/components/HeroSection';
|
||||
|
||||
describe(__filename, () => {
|
||||
function shallowRender({ ...props } = {}) {
|
||||
return shallow(<HeroSection {...props} />);
|
||||
}
|
||||
|
||||
it('renders a HeroSection', () => {
|
||||
const root = shallowRender();
|
||||
|
||||
expect(root).toHaveClassName('HeroSection');
|
||||
});
|
||||
|
||||
it('renders a className for the styleName', () => {
|
||||
const root = shallowRender({ styleName: 'Home-privacy-matters' });
|
||||
|
||||
expect(root).toHaveClassName('HeroSection-styleName--Home-privacy-matters');
|
||||
});
|
||||
|
||||
it('renders default styleName className if styleName is undefined', () => {
|
||||
const root = shallowRender({ styleName: undefined });
|
||||
|
||||
expect(root).toHaveClassName('HeroSection-styleName--default');
|
||||
});
|
||||
|
||||
it('renders a Link if linkTo prop is supplied', () => {
|
||||
const linkTo = '/somewhere/';
|
||||
const root = shallowRender({ linkTo });
|
||||
const link = root.find(Link);
|
||||
|
||||
expect(link).toHaveProp('className', 'HeroSection-link-wrapper');
|
||||
expect(link).toHaveProp('to', linkTo);
|
||||
expect(root.find('.HeroSection-wrapper')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('renders children inside a Link when there is a linkTo prop', () => {
|
||||
const root = shallowRender({ children: 'hello!', linkTo: '/homepage/' });
|
||||
|
||||
expect(root.find(Link)).toHaveLength(1);
|
||||
expect(root.find(Link).find('.HeroSection-content')).toHaveProp(
|
||||
'children',
|
||||
'hello!',
|
||||
);
|
||||
});
|
||||
|
||||
it('renders a div if linkTo prop is not supplied', () => {
|
||||
const root = shallowRender();
|
||||
|
||||
expect(root.find('.HeroSection-link-wrapper')).toHaveLength(0);
|
||||
expect(root.find('.HeroSection-wrapper')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders a children inside div with no links', () => {
|
||||
const root = shallowRender({ children: 'hello!' });
|
||||
|
||||
expect(
|
||||
root.find('.HeroSection-wrapper').find('.HeroSection-content'),
|
||||
).toHaveText('hello!');
|
||||
});
|
||||
});
|
|
@ -1,137 +0,0 @@
|
|||
import knuthShuffle from 'knuth-shuffle';
|
||||
|
||||
import heroBannerOrderReducer, {
|
||||
initialState,
|
||||
setHeroBannerOrder,
|
||||
} from 'amo/reducers/heroBanners';
|
||||
|
||||
describe(__filename, () => {
|
||||
const defaultParams = { name: 'MegaHero', sections: ['1', '2', '3'] };
|
||||
|
||||
it('defaults to empty', () => {
|
||||
const state = heroBannerOrderReducer(initialState, {});
|
||||
|
||||
expect(state).toEqual({});
|
||||
});
|
||||
|
||||
it('creates a new key if one is not found', () => {
|
||||
const knuthShuffleSpy = sinon.spy(knuthShuffle, 'knuthShuffle');
|
||||
const state = heroBannerOrderReducer(
|
||||
initialState,
|
||||
setHeroBannerOrder({
|
||||
name: 'NotAKnownKey1234',
|
||||
sections: [1, 2, 3],
|
||||
}),
|
||||
);
|
||||
|
||||
sinon.assert.notCalled(knuthShuffleSpy);
|
||||
expect(state).toMatchObject({
|
||||
NotAKnownKey1234: { order: [0, 1, 2] },
|
||||
});
|
||||
});
|
||||
|
||||
it('randomizes the order if random prop is passed', () => {
|
||||
const knuthShuffleSpy = sinon.spy(knuthShuffle, 'knuthShuffle');
|
||||
const state = heroBannerOrderReducer(
|
||||
initialState,
|
||||
setHeroBannerOrder({
|
||||
name: 'CoolPage',
|
||||
random: true,
|
||||
sections: [1, 2, 3],
|
||||
}),
|
||||
);
|
||||
|
||||
sinon.assert.called(knuthShuffleSpy);
|
||||
expect(state).toMatchObject({
|
||||
CoolPage: { order: expect.arrayContaining([0, 1, 2]) },
|
||||
});
|
||||
});
|
||||
|
||||
it('does not modify the original sections data', () => {
|
||||
const knuthShuffleSpy = sinon.spy(knuthShuffle, 'knuthShuffle');
|
||||
const sections = [1, 2, 3, 5];
|
||||
heroBannerOrderReducer(
|
||||
initialState,
|
||||
setHeroBannerOrder({
|
||||
name: 'CoolPage',
|
||||
random: true,
|
||||
sections,
|
||||
}),
|
||||
);
|
||||
|
||||
sinon.assert.called(knuthShuffleSpy);
|
||||
expect(sections).toEqual([1, 2, 3, 5]);
|
||||
});
|
||||
|
||||
it('limits the number of sections to three', () => {
|
||||
const state = heroBannerOrderReducer(
|
||||
initialState,
|
||||
setHeroBannerOrder({
|
||||
name: 'CoolPage',
|
||||
sections: [1, 2, 3, 4, 5],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(state).toMatchObject({
|
||||
CoolPage: { order: expect.arrayContaining([0, 1, 2]) },
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts fewer than three sections', () => {
|
||||
const state = heroBannerOrderReducer(
|
||||
initialState,
|
||||
setHeroBannerOrder({
|
||||
name: 'CoolPage',
|
||||
sections: [1, 2],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(state).toMatchObject({
|
||||
CoolPage: { order: expect.arrayContaining([0, 1]) },
|
||||
});
|
||||
});
|
||||
|
||||
it('randomizes first and then picks three sections', () => {
|
||||
const sections = [0, 1, 2, 3, 4, 5];
|
||||
// Our sort algorithm is reverse ordering.
|
||||
const knuthShuffleSpy = sinon
|
||||
.stub(knuthShuffle, 'knuthShuffle')
|
||||
.callsFake((sectionsToSort) => {
|
||||
return sectionsToSort.reverse();
|
||||
});
|
||||
|
||||
const state = heroBannerOrderReducer(
|
||||
initialState,
|
||||
setHeroBannerOrder({
|
||||
name: 'CoolPage',
|
||||
random: true,
|
||||
sections,
|
||||
}),
|
||||
);
|
||||
|
||||
sinon.assert.called(knuthShuffleSpy);
|
||||
expect(state).toMatchObject({
|
||||
CoolPage: { order: [5, 4, 3] },
|
||||
});
|
||||
});
|
||||
|
||||
describe('carousel actions', () => {
|
||||
it('requires name', () => {
|
||||
const partialParams = { ...defaultParams };
|
||||
delete partialParams.name;
|
||||
|
||||
expect(() => {
|
||||
setHeroBannerOrder(partialParams);
|
||||
}).toThrow(/name is required/);
|
||||
});
|
||||
|
||||
it('requires sections', () => {
|
||||
const partialParams = { ...defaultParams };
|
||||
delete partialParams.sections;
|
||||
|
||||
expect(() => {
|
||||
setHeroBannerOrder(partialParams);
|
||||
}).toThrow(/sections are required/);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -7523,11 +7523,6 @@ known-css-properties@^0.21.0:
|
|||
resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.21.0.tgz#15fbd0bbb83447f3ce09d8af247ed47c68ede80d"
|
||||
integrity sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==
|
||||
|
||||
knuth-shuffle@1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/knuth-shuffle/-/knuth-shuffle-1.0.8.tgz#929a467b0efd8d297bdcf318ca988a9f1037f80d"
|
||||
integrity sha512-IdC4Hpp+mx53zTt6VAGsAtbGM0g4BV9fP8tTcviCosSwocHcRDw9uG5Rnv6wLWckF4r72qeXFoK9NkvV1gUJCQ==
|
||||
|
||||
language-subtag-registry@~0.3.2:
|
||||
version "0.3.21"
|
||||
resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a"
|
||||
|
|
Загрузка…
Ссылка в новой задаче