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

Partially registries merge #384

Merged
merged 2 commits into from
Jan 10, 2019
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
22 changes: 19 additions & 3 deletions packages/di/di.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export function withRegistry(...registries: Registry[]) {
const overrides = contextRegistries[registry.id];

providedRegistries[registry.id] = registry.inverted
? (overrides || registry)
: (registry || overrides);
? overrides ? registry.merge(overrides) : registry
: (registry && overrides) ? overrides.merge(registry) : registry;
});

return (
Expand Down Expand Up @@ -63,7 +63,6 @@ interface IRegistryComponents {
export class Registry {
id: string;
inverted: boolean;

private components: IRegistryComponents = {};

constructor({ id, inverted = false }: IRegistryOptions) {
Expand Down Expand Up @@ -98,7 +97,24 @@ export class Registry {
return this.components[id];
}

/**
* Returns list of components from registry.
*/
snapshot<RT>(): RT {
return this.components as any;
}

/**
* Override components by external registry.
*
* @param registry external registry
*/
merge(registry: Registry) {
this.components = {
...this.components,
...(registry ? registry.snapshot() : {}),
};

return this;
}
}
224 changes: 215 additions & 9 deletions packages/di/test/di.test.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,231 @@
// tslint:disable no-shadowed-variable
import React from 'react';
import { describe, it } from 'mocha';
import { expect } from 'chai';
import { render } from 'enzyme';

import { Registry } from '../di';
import { Registry, withRegistry, RegistryConsumer, ComponentRegistryConsumer } from '../di';

interface ICommonProps {
className?: string;
}

describe('@bem-react/di', () => {
describe('Registry', () => {
it('should set components and return this by id', () => {
const registry = new Registry({ id: 'id' });
it('should set and components by id', () => {
const registry = new Registry({ id: 'registry' });
const Component1 = () => null;
const Component2 = () => <span/>;

registry
.set('id-1', Component1)
.set('id-2', Component2);

expect(registry.get('id-1')).to.eq(Component1);
expect(registry.get('id-2')).to.eq(Component2);
});

it('should return list of components', () => {
const registry = new Registry({ id: 'registry' });
const Component1 = () => null;
const Component2 = () => <span/>;

registry
.set('id-1', Component1)
.set('id-2', Component2);

const snapshot = {
'id-1': Component1,
'id-2': Component2,
};

expect(registry.snapshot()).to.eql(snapshot);
});

it('should merge registries', () => {
const registry = new Registry({ id: 'registry' });
const Component1 = () => null;
const Component2 = () => null;
const Component2 = () => <span/>;

registry
.set('id-1', Component1)
.set('id-2', Component2);

expect(registry.get('id-1')).to.equal(Component1);
expect(registry.get('id-2')).to.equal(Component2);
const overrides = new Registry({ id: 'overrides' });
const Component1Overrided = () => <div/>;

overrides.set('id-1', Component1Overrided);

const snapshot = {
'id-1': Component1Overrided,
'id-2': Component2,
};

expect(registry.merge(overrides).snapshot()).to.eql(snapshot);
});

it('should not affect registry in merge with undefined', () => {
Copy link
Member

Choose a reason for hiding this comment

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

Какой смысл от такого теста?

Copy link
Member Author

Choose a reason for hiding this comment

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

В покрытии 100% ;)))

const registry = new Registry({ id: 'registry' });
const Component1 = () => null;
const Component2 = () => <span/>;

registry
.set('id-1', Component1)
.set('id-2', Component2);

const snapshot = {
'id-1': Component1,
'id-2': Component2,
};

// @ts-ignore to check inside logic
expect(registry.merge().snapshot()).to.eql(snapshot);
});

it('should throw error when component doesn\'t exist', () => {
const registry = new Registry({ id: 'registry' });

expect(() => registry.get('id')).to.throw('Component with id \'id\' not found.');
});
});

describe.skip('withRegistry', () => {
// TODO: Add test for withRegistry
return null;
describe('withRegistry', () => {
it('should provide registry to context', () => {
const compositorRegistry = new Registry({ id: 'Compositor' });
const Element: React.SFC<ICommonProps> = () => <span>content</span>;

interface ICompositorRegistry {
Element: React.ComponentType<ICommonProps>;
}

compositorRegistry.set('Element', Element);

const CompositorPresenter: React.SFC<ICommonProps> = () => (
<RegistryConsumer>
{registries => {
const registry = registries['Compositor'];
const { Element } = registry.snapshot<ICompositorRegistry>();

return <Element/>;
}}
</RegistryConsumer>
);

const Compositor = withRegistry(compositorRegistry)(CompositorPresenter);

expect(render(<Compositor/>).text()).eq('content');
});

it('should provide assign registry with component', () => {
const compositorRegistry = new Registry({ id: 'Compositor' });
const Element: React.SFC<ICommonProps> = () => <span>content</span>;

interface ICompositorRegistry {
Element: React.ComponentType<ICommonProps>;
}

compositorRegistry.set('Element', Element);

const CompositorPresenter: React.SFC<ICommonProps> = () => (
<ComponentRegistryConsumer id="Compositor">
{({ Element }: ICompositorRegistry) => <Element/>}
</ComponentRegistryConsumer>
);

const Compositor = withRegistry(compositorRegistry)(CompositorPresenter);

expect(render(<Compositor/>).text()).eq('content');
});

it('should override components in registry by context', () => {
const compositorRegistry = new Registry({ id: 'Compositor' });
const Element: React.SFC<ICommonProps> = () => <span>content</span>;

const overridedCompositorRegistry = new Registry({ id: 'Compositor' });
const OverridedElement: React.SFC<ICommonProps> = () => <span>overrided</span>;

interface ICompositorRegistry {
Element: React.ComponentType<ICommonProps>;
}

compositorRegistry.set('Element', Element);
overridedCompositorRegistry.set('Element', OverridedElement);

const CompositorPresenter: React.SFC<ICommonProps> = () => {
const Content: React.SFC<ICommonProps> = withRegistry(overridedCompositorRegistry)(() => (
<ComponentRegistryConsumer id="Compositor">
{({ Element }: ICompositorRegistry) => <Element/>}
</ComponentRegistryConsumer>
));

return <Content/>;
};

const Compositor = withRegistry(compositorRegistry)(CompositorPresenter);

expect(render(<Compositor/>).text()).eq('overrided');
});

it('should override components in registry from top node', () => {
const compositorRegistry = new Registry({ id: 'Compositor', inverted: true });
const Element: React.SFC<ICommonProps> = () => <span>content</span>;

const overridedCompositorRegistry = new Registry({ id: 'Compositor' });
const OverridedElement: React.SFC<ICommonProps> = () => <span>overrided</span>;

interface ICompositorRegistry {
Element: React.ComponentType<ICommonProps>;
}

compositorRegistry.set('Element', Element);
overridedCompositorRegistry.set('Element', OverridedElement);

const CompositorPresenter: React.SFC<ICommonProps> = () => (
<ComponentRegistryConsumer id="Compositor">
{({ Element }: ICompositorRegistry) => <Element/>}
</ComponentRegistryConsumer>
);

const Compositor = withRegistry(compositorRegistry)(CompositorPresenter);
const OverridedCompositor = withRegistry(overridedCompositorRegistry)(Compositor);

expect(render(<Compositor/>).text()).eq('content');
expect(render(<OverridedCompositor/>).text()).eq('overrided');
});

it('should partially override components in registry', () => {
const compositorRegistry = new Registry({ id: 'Compositor', inverted: true });
const Element1: React.SFC<ICommonProps> = () => <span>content</span>;
const Element2: React.SFC<ICommonProps> = () => <span>extra</span>;

const overridedCompositorRegistry = new Registry({ id: 'Compositor' });
const OverridedElement: React.SFC<ICommonProps> = () => <span>overrided</span>;

interface ICompositorRegistry {
Element1: React.ComponentType<ICommonProps>;
Element2: React.ComponentType<ICommonProps>;
}

compositorRegistry.set('Element1', Element1);
compositorRegistry.set('Element2', Element2);
overridedCompositorRegistry.set('Element1', OverridedElement);

const CompositorPresenter: React.SFC<ICommonProps> = () => (
<ComponentRegistryConsumer id="Compositor">
{({ Element1, Element2 }: ICompositorRegistry) => (
<>
<Element1/>
<Element2/>
</>
)}
</ComponentRegistryConsumer>
);

const Compositor = withRegistry(compositorRegistry)(CompositorPresenter);
const OverridedCompositor = withRegistry(overridedCompositorRegistry)(Compositor);

expect(render(<Compositor/>).text()).eq('contentextra');
expect(render(<OverridedCompositor/>).text()).eq('overridedextra');
});
});
});