Skip to content

Commit

Permalink
feat(ts): convert poweredBy widget (#4756)
Browse files Browse the repository at this point in the history
* feat(ts): convert poweredBy widget

Co-authored-by: François Chalifour <[email protected]>
  • Loading branch information
shortcuts and francoischalifour authored May 18, 2021
1 parent 4a9ac35 commit 142660a
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 106 deletions.
13 changes: 6 additions & 7 deletions src/components/PoweredBy/PoweredBy.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
/** @jsx h */

import { h } from 'preact';
import { PoweredByCSSClasses } from '../../widgets/powered-by/powered-by';

type CSSClasses = {
root: string;
link: string;
logo: string;
export type PoweredByComponentCSSClasses = {
[TClassName in keyof PoweredByCSSClasses]: string;
};

type Props = {
export type PoweredByProps = {
url: string;
theme: string;
cssClasses: CSSClasses;
cssClasses: PoweredByComponentCSSClasses;
};

const PoweredBy = ({ url, theme, cssClasses }: Props) => (
const PoweredBy = ({ url, theme, cssClasses }: PoweredByProps) => (
<div className={cssClasses.root}>
<a
href={url}
Expand Down

This file was deleted.

52 changes: 0 additions & 52 deletions src/widgets/powered-by/__tests__/powered-by-test.js

This file was deleted.

74 changes: 74 additions & 0 deletions src/widgets/powered-by/__tests__/powered-by-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import algoliasearchHelper, { AlgoliaSearchHelper } from 'algoliasearch-helper';
import { render as preactRender, VNode } from 'preact';
import { createSearchClient } from '../../../../test/mock/createSearchClient';
import {
createInitOptions,
createRenderOptions,
} from '../../../../test/mock/createWidget';
import { castToJestMock } from '../../../../test/utils/castToJestMock';
import { PoweredByProps } from '../../../components/PoweredBy/PoweredBy';
import poweredBy from '../powered-by';

const render = castToJestMock(preactRender);
jest.mock('preact', () => {
const module = jest.requireActual('preact');

module.render = jest.fn();

return module;
});

describe('poweredBy call', () => {
it('throws an exception when no container', () => {
expect(poweredBy).toThrowErrorMatchingInlineSnapshot(`
"The \`container\` option is required.
See documentation: https://www.algolia.com/doc/api-reference/widgets/powered-by/js/"
`);
});
});

describe('poweredBy', () => {
let widget: ReturnType<typeof poweredBy>;
let container: HTMLElement;
let helper: AlgoliaSearchHelper;

beforeEach(() => {
render.mockClear();

container = document.createElement('div');
widget = poweredBy({
container,
cssClasses: {
root: 'root',
link: 'link',
logo: 'logo',
},
});

helper = algoliasearchHelper(createSearchClient(), '', {});

widget.init!(createInitOptions({ helper }));
});

it('renders only once at init', () => {
widget.render!(createRenderOptions({ helper }));
widget.render!(createRenderOptions({ helper }));

const firstRender = render.mock.calls[0][0] as VNode<PoweredByProps>;
const firstContainer = render.mock.calls[0][1];

expect(render).toHaveBeenCalledTimes(1);
expect(firstRender.props).toEqual({
cssClasses: {
link: 'ais-PoweredBy-link link',
logo: 'ais-PoweredBy-logo logo',
root: 'ais-PoweredBy ais-PoweredBy--light root',
},
theme: 'light',
url:
'https://www.algolia.com/?utm_source=instantsearch.js&utm_medium=website&utm_content=localhost&utm_campaign=poweredby',
});
expect(firstContainer).toEqual(container);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,30 @@
import { h, render } from 'preact';
import cx from 'classnames';
import PoweredBy from '../../components/PoweredBy/PoweredBy';
import connectPoweredBy from '../../connectors/powered-by/connectPoweredBy';
import connectPoweredBy, {
PoweredByConnectorParams,
PoweredByRenderState,
PoweredByWidgetDescription,
} from '../../connectors/powered-by/connectPoweredBy';
import {
getContainerNode,
createDocumentationMessageGenerator,
} from '../../lib/utils';
import { component } from '../../lib/suit';
import { Renderer, WidgetFactory } from '../../types';

const suit = component('PoweredBy');
const withUsage = createDocumentationMessageGenerator({ name: 'powered-by' });

const renderer = ({ containerNode, cssClasses }) => (
const renderer = ({
containerNode,
cssClasses,
}): Renderer<PoweredByRenderState, Partial<PoweredByWidgetParams>> => (
{ url, widgetParams },
isFirstRendering
) => {
if (isFirstRendering) {
const { theme } = widgetParams;
const { theme = 'light' } = widgetParams;

render(
<PoweredBy cssClasses={cssClasses} url={url} theme={theme} />,
Expand All @@ -29,36 +37,48 @@ const renderer = ({ containerNode, cssClasses }) => (
}
};

/**
* @typedef {Object} PoweredByWidgetCssClasses
* @property {string|string[]} [root] CSS classes added to the root element of the widget.
* @property {string|string[]} [link] CSS class to add to the link.
* @property {string|string[]} [logo] CSS class to add to the SVG logo.
*/

/**
* @typedef {Object} PoweredByWidgetParams
* @property {string|HTMLElement} container Place where to insert the widget in your webpage.
* @property {string} [theme] The theme of the logo ("light" or "dark").
* @property {PoweredByWidgetCssClasses} [cssClasses] CSS classes to add.
*/

/**
* The `poweredBy` widget is used to display the logo to redirect to Algolia.
* @type {WidgetFactory}
* @devNovel PoweredBy
* @category metadata
* @param {PoweredByWidgetParams} widgetParams PoweredBy widget options. Some keys are mandatory: `container`,
* @return {Widget} A new poweredBy widget instance
* @example
* search.addWidgets([
* instantsearch.widgets.poweredBy({
* container: '#poweredBy-container',
* theme: 'dark',
* })
* ]);
*/
export default function poweredBy(widgetParams) {
export type PoweredByCSSClasses = {
/**
* CSS class to add to the wrapping element.
*/
root: string | string[];

/**
* CSS class to add to the link.
*/
link: string | string[];

/**
* CSS class to add to the SVG logo.
*/
logo: string | string[];
};

export type PoweredByWidgetParams = {
/**
* CSS Selector or HTMLElement to insert the widget.
*/
container: string | HTMLElement;

/**
* The theme of the logo.
* @default 'light'
*/
theme?: 'light' | 'dark';

/**
* CSS classes to add.
*/
cssClasses?: Partial<PoweredByCSSClasses>;
};

export type PoweredByWidget = WidgetFactory<
PoweredByWidgetDescription & { $$widgetType: 'ais.poweredBy' },
PoweredByConnectorParams,
PoweredByWidgetParams
>;

const poweredBy: PoweredByWidget = function poweredBy(widgetParams) {
const { container, cssClasses: userCssClasses = {}, theme = 'light' } =
widgetParams || {};

Expand Down Expand Up @@ -91,4 +111,6 @@ export default function poweredBy(widgetParams) {
...makeWidget({ theme }),
$$widgetType: 'ais.poweredBy',
};
}
};

export default poweredBy;

0 comments on commit 142660a

Please sign in to comment.