Skip to content

Commit

Permalink
Refactor ControlOption and improvements
Browse files Browse the repository at this point in the history
- Add missed `className`s properties to components
- Introduce `assert` function
- Improve public modules exposure
- Increase code coverage
  • Loading branch information
widoz authored Feb 15, 2024
1 parent d625df0 commit 9acdf8f
Show file tree
Hide file tree
Showing 17 changed files with 12,732 additions and 16,750 deletions.
6 changes: 5 additions & 1 deletion sources/client/src/components/toggle-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,15 @@ export function ToggleControl(
const value = String(option.value);
const id = idByControlOption(option);
return (
<div key={value} className="wz-toggle-control-item">
<div
key={value}
className={`wz-toggle-control-item wz-toggle-control-item--${option.value}`}
>
<label htmlFor={id}>
<input
type="checkbox"
id={id}
className={`wz-toggle-control-item__input-${option.value}`}
checked={props.value?.has(option.value)}
value={value}
onChange={onChange}
Expand Down
7 changes: 6 additions & 1 deletion sources/client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './api/fetch';
export * from './api/search-entities';

export * from './components/composite-entities-by-kind';
Expand All @@ -8,10 +7,16 @@ export * from './components/search-control';
export * from './components/singular-select-control';
export * from './components/toggle-control';

export * from './hooks/use-search';
export * from './hooks/use-entity-records';
export * from './hooks/use-query-viewable-post-types';
export * from './hooks/use-query-viewable-taxonomies';

export * from './utils/convert-entities-to-control-options';
export * from './utils/is-control-option';
export * from './utils/order-selected-options-at-the-top';
export * from './utils/slugify-option-label';
export * from './utils/unique-control-options';

export * from './vo/control-option';
export * from './vo/set';
5 changes: 5 additions & 0 deletions sources/client/src/utils/assert.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function assert(condition: boolean, message: string): asserts condition {
if (!condition) {
throw new Error(message);
}
}
15 changes: 4 additions & 11 deletions sources/client/src/utils/convert-entities-to-control-options.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import EntitiesSearch from '@types';

import { ControlOption } from '../vo/control-option';
import { Set } from '../vo/set';
import { makeControlOption } from './make-control-option';
import { assert } from './assert';

export function convertEntitiesToControlOptions<
V,
Expand All @@ -14,15 +15,7 @@ export function convertEntitiesToControlOptions<
return entities.map((entity) => {
const label = entity[labelKey];
const value = entity[valueKey];
labelKeyIsString(label);
return makeControlOption(label, value);
assert(typeof label === 'string', 'Label Key must be a string');
return new ControlOption(label, value);
});
}

function labelKeyIsString(label: unknown): asserts label is string {
if (typeof label !== 'string') {
throw new Error(
'convertEntitiesToControlOptions: Label Key must be a string'
);
}
}
9 changes: 0 additions & 9 deletions sources/client/src/utils/make-control-option.ts

This file was deleted.

22 changes: 22 additions & 0 deletions sources/client/src/vo/control-option.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import EntitiesSearch from '@types';

import { assert } from '../utils/assert';

export class ControlOption<V> implements EntitiesSearch.ControlOption<V> {
public readonly label: string;
public readonly value: V;

public constructor(label: string, value: V) {
assert(
label !== '',
'ControlOption: Label must be a non empty string.'
);
assert(
value !== '',
'ControlOption: Value must be a non empty string.'
);

this.label = label;
this.value = value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Posts Select Render the NoOptionsMessage component 1`] = `
<DocumentFragment>
<p
class="wp-entities-search-no-option-message"
>
No options
</p>
</DocumentFragment>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ exports[`EntitiesToggleControl renders correctly 1`] = `
class="test-class wz-toggle-control"
>
<div
class="wz-toggle-control-item"
class="wz-toggle-control-item wz-toggle-control-item--1"
>
<label
for="wz-toggle-control-item__input-option-1-1"
>
<input
checked=""
class="wz-toggle-control-item__input-1"
id="wz-toggle-control-item__input-option-1-1"
type="checkbox"
value="1"
Expand All @@ -20,12 +21,13 @@ exports[`EntitiesToggleControl renders correctly 1`] = `
</label>
</div>
<div
class="wz-toggle-control-item"
class="wz-toggle-control-item wz-toggle-control-item--2"
>
<label
for="wz-toggle-control-item__input-option-2-2"
>
<input
class="wz-toggle-control-item__input-2"
id="wz-toggle-control-item__input-option-2-2"
type="checkbox"
value="2"
Expand All @@ -34,12 +36,13 @@ exports[`EntitiesToggleControl renders correctly 1`] = `
</label>
</div>
<div
class="wz-toggle-control-item"
class="wz-toggle-control-item wz-toggle-control-item--3"
>
<label
for="wz-toggle-control-item__input-option-3-3"
>
<input
class="wz-toggle-control-item__input-3"
id="wz-toggle-control-item__input-option-3-3"
type="checkbox"
value="3"
Expand Down
12 changes: 12 additions & 0 deletions tests/client/unit/components/plural-select-control.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,16 @@ describe('Posts Select', () => {
await userEvent.deselectOptions(select, valuesToSelect);
expect(select.selectedOptions.length).toBe(0);
});

it('Render the NoOptionsMessage component', () => {
const rendered = render(
<PluralSelectControl
options={new Set()}
value={new Set()}
onChange={() => {}}
/>
);

expect(rendered.asFragment()).toMatchSnapshot();
});
});
25 changes: 25 additions & 0 deletions tests/client/unit/components/radio-control.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,29 @@ describe('KindRadioControl', () => {

expect(props.onChange).toHaveBeenCalledWith('option-2');
});

it('does not change the value when an option is selected that does not exist', () => {
const props = {
options: new Set([
{
label: 'Option 1',
value: 'option-one',
},
{
label: 'Option 2',
value: 'option-two',
},
]),
value: 'option-two',
onChange: jest.fn(),
};
render(<RadioControl {...props} />);

const option = screen.getByLabelText<HTMLInputElement>('Option 1');
option.value = 'option-3';

fireEvent.click(option);

expect(props.onChange).not.toHaveBeenCalled();
});
});
33 changes: 32 additions & 1 deletion tests/client/unit/components/singular-select-control.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import EntitiesSearch from '@types';
import React from 'react';

import { describe, it, expect } from '@jest/globals';
import { describe, it, expect, jest } from '@jest/globals';

import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
Expand Down Expand Up @@ -60,4 +60,35 @@ describe('Post Types Select', () => {
)
).toBeInTheDocument();
});

it('does not change the value when an option is selected that does not exist', async () => {
const props = {
options: new Set([
{
label: 'Option 1',
value: 'option-one',
},
{
label: 'Option 2',
value: 'option-two',
},
]),
value: 'option-two',
onChange: jest.fn(),
};
const rendered = render(<SingularSelectControl {...props} />);

const select = rendered.container.querySelector(
'.wz-select-control'
) as HTMLSelectElement;

const option = select.querySelector(
'.wz-select-control-item--option-one'
) as HTMLOptionElement;
option.value = 'option-3';

await userEvent.selectOptions(select, option);

expect(props.onChange).not.toHaveBeenCalled();
});
});
28 changes: 28 additions & 0 deletions tests/client/unit/components/toggle-control.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import { describe, expect, it, jest } from '@jest/globals';

import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { ToggleControl } from '../../../../sources/client/src/components/toggle-control';
import { Set } from '../../../../sources/client/src/vo/set';
Expand Down Expand Up @@ -73,4 +74,31 @@ describe('EntitiesToggleControl', () => {

expect(onChange).toHaveBeenCalledWith(new Set(['2']));
});

it('does not change the value when an option is selected that does not exist', async () => {
const props = {
options: new Set([
{
label: 'Option 1',
value: 'option-one',
},
{
label: 'Option 2',
value: 'option-two',
},
]),
value: new Set(['option-one']),
onChange: jest.fn(),
};
const rendered = render(<ToggleControl {...props} />);

const option = rendered.container.querySelector(
'.wz-toggle-control-item__input-option-one'
) as HTMLOptionElement;
option.value = 'option-3';

await userEvent.click(option);

expect(props.onChange).not.toHaveBeenCalled();
});
});
45 changes: 45 additions & 0 deletions tests/client/unit/storage/reducer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,49 @@ describe('reducer', () => {

expect(newState).toEqual(state);
});

it('update search phrase', () => {
const searchPhrase = faker.lorem.word();
const newState = reducer(state, {
type: 'UPDATE_SEARCH_PHRASE',
searchPhrase,
});

expect(newState.searchPhrase).toEqual(searchPhrase);
});

it('clean entities options', () => {
const newState = reducer(state, {
type: 'CLEAN_ENTITIES_OPTIONS',
});

expect(newState.selectedEntitiesOptions).toEqual(new Set());
expect(newState.contextualEntitiesOptions).toEqual(new Set());
expect(newState.currentEntitiesOptions).toEqual(new Set());
});

it('update entities options for new kind', () => {
const kind = new Set(['post']);
const entitiesOptions = new Set([
{
value: faker.number.int(10),
label: 'Post One',
},
{
value: faker.number.int({ min: 11, max: 20 }),
label: 'Post Two',
},
]);
const newState = reducer(state, {
type: 'UPDATE_ENTITIES_OPTIONS_FOR_NEW_KIND',
kind,
entitiesOptions,
});

expect(newState.contextualEntitiesOptions).toEqual(entitiesOptions);
expect(newState.currentEntitiesOptions).toEqual(entitiesOptions);
expect(newState.selectedEntitiesOptions).toEqual(new Set());
expect(newState.entities).toEqual(new Set());
expect(newState.kind).toEqual(kind);
});
});
16 changes: 16 additions & 0 deletions tests/client/unit/utils/assert.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Write tests cases to ensure the assert function behaves as expected
*/
import { expect, it, describe } from '@jest/globals';

import { assert } from '../../../../sources/client/src/utils/assert';

describe('assert', () => {
it('should throw an error if the condition is false', () => {
expect(() => assert(false, 'Failed')).toThrow();
});

it('should not throw an error if the condition is true', () => {
expect(() => assert(true, 'Failed')).not.toThrow();
});
});
13 changes: 0 additions & 13 deletions tests/client/unit/utils/make-control-option.test.ts

This file was deleted.

Loading

0 comments on commit 9acdf8f

Please sign in to comment.