Skip to content

Commit

Permalink
inputtime
Browse files Browse the repository at this point in the history
  • Loading branch information
tborychowski committed Sep 26, 2023
1 parent 2820f29 commit 029e1fd
Show file tree
Hide file tree
Showing 16 changed files with 347 additions and 116 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Changelog
=========

## v9.1.0 *(2023-09-?)*
- New components: `InputRating`, `Tag`, `InputTag`.
- New components: `InputRating`, `Tag`, `InputTag`, `InputTime`.
- Add `hideTip` and more, to `Popover`.
- Small bugfixes and improvements.

Expand Down
1 change: 1 addition & 0 deletions docs-src/components/input/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from './input-password';
export * from './input-rating';
export * from './input-search';
export * from './input-tag';
export * from './input-time';
export * from './input-text';
export * from './radio';
export * from './select';
Expand Down
74 changes: 74 additions & 0 deletions docs-src/components/input/input-time/InputTime.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<h2>Input Time</h2>
<p>This is just a simple wrapper around the native input time control.</p>


<h3>Normal</h3>
<InputTime bind:value="{val}" />
<p>Input value: {val}</p>

<h3>Disabled</h3>
<InputTime disabled value="00:00"/>


<h3>With validation</h3>
<InputTime
label="Select Midnight"
error="{error1}"
value="00:01"
on:change="{onchange}" />


<h3>Label on the left</h3>
<InputTime label="Label is on the left" labelOnTheLeft="true"/>



<CodeExample html="{exampleHtml}" />

<API props="{apiProps}"/>


<script>
import { InputTime } from '../../../../src';
import { CodeExample } from '../../../code-example';
import { API } from '../../../api-table';
const apiProps = [
{ name: 'class', type: 'string', description: 'Additional css class name to be added to the component.' },
{ name: 'disabled', description: 'Make the input disabled.' },
{ name: 'id', type: 'string', description: 'Assign ID to the underlying input.' },
{ name: 'info', type: 'string', description: 'Show info message above the input.' },
{ name: 'error', type: 'string', description: 'Error message to show above the input.' },
{ name: 'name', type: 'string', description: 'Assign title to the underlying input.' },
{ name: 'label', type: 'string', description: 'Label for the input.' },
{ name: 'labelOnTheLeft', type: ['true', 'false'], default: 'false', description: 'Put label to the left of the input (instead of at the top). Usually in longer forms, to align labels and inputs, hence input also gets <em>width: 100%</em>, as it will be constraint by the form container.' },
{ name: 'placeholder', type: 'string', description: 'Assign placeholder to the underlying input.' },
{ name: 'required', description: 'Mark the input as <i>aria-required</i>. The actual validation must be done in the consumer.' },
{ name: 'title', type: 'string', description: 'Assign title to the underlying input.' },
{ name: 'value', type: ['string', 'number'], description: 'Initial value of the input.' },
{ name: 'bind:element', type: 'element', description: 'Exposes the HTML element of the component.' },
{ name: 'bind:inputElement', type: 'element', description: 'Exposes the HTML element of the underlying input.' },
{ name: 'on:change', type: 'function', description: 'Triggered after the value changes and the focus leaves the input.' },
{ name: 'on:input', type: 'function', description: 'Triggered as soon as the input value changes.' },
];
const exampleHtml = `
<InputTime label="Email" error="Invalid email" value="00:00" on:change="{onChange}" />
<script>
function onChange (e) {
console.log('value', e.target.value);
}
&lt;/script>
`;
let val = '00:00';
let error1 = 'Select midnight please.';
function onchange (e) {
error1 = (e.target.value === '00:00' ? '' : 'Select midnight please.');
console.log(e.target.value);
}
</script>
1 change: 1 addition & 0 deletions docs-src/components/input/input-time/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as InputTime } from './InputTime.svelte';
1 change: 1 addition & 0 deletions docs-src/nav/Nav.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<NavItem name="Input Search" {active} />
<NavItem name="Input Tag" {active} />
<NavItem name="Input Text" {active} />
<NavItem name="Input Time" {active} />
<NavItem name="Radio" {active} />
<NavItem name="Select" {active} />
<NavItem name="Textarea" {active} />
Expand Down
2 changes: 1 addition & 1 deletion docs-src/pages/changelog.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<h1>Changelog</h1>
<h2>v9.1.0 <em>(2023-09-?)</em></h2>
<ul>
<li>New components: <code>InputRating</code>, <code>Tag</code>, <code>InputTag</code>.</li>
<li>New components: <code>InputRating</code>, <code>Tag</code>, <code>InputTag</code>, <code>InputTime</code>.</li>
<li>Add <code>hideTip</code> and more, to <code>Popover</code>.</li>
<li>Small bugfixes and improvements.</li>
</ul>
Expand Down
212 changes: 110 additions & 102 deletions docs/docs.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/ui.css

Large diffs are not rendered by default.

21 changes: 11 additions & 10 deletions src/icon/icons.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/input/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './input-password';
export * from './input-rating';
export * from './input-search';
export * from './input-tag';
export * from './input-time';
export * from './input-text';
export * from './label';
export * from './radio';
Expand Down
3 changes: 3 additions & 0 deletions src/input/input-time/InputTime.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.input-time input {
padding-left: calc(2rem + 6px);
}
64 changes: 64 additions & 0 deletions src/input/input-time/InputTime.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<div
class="input input-time {className}"
class:has-error="{error}"
class:has-value="{value !== ''}"
class:label-on-the-left="{labelOnTheLeft === true || labelOnTheLeft === 'true'}"
bind:this="{element}">

<Label {label} {disabled} for="{_id}"/>
<Info msg="{info}" />

<div class="input-inner" class:disabled>
<InputError id="{errorMessageId}" msg="{error}" />

<div class="input-row">
<Icon name="clock"/>

<input
autocomplete="off"
type="time"
{...props}
{disabled}
id="{_id}"
aria-invalid="{error}"
aria-errormessage="{error ? errorMessageId : undefined}"
aria-required="{required}"
bind:this="{inputElement}"
bind:value="{value}"
on:input
on:change
on:focus
on:blur>
</div>
</div>
</div>

<script>
import { pluck, guid } from '../../utils';
import { Icon } from '../../icon';
import { Info } from '../../info-bar';
import { InputError } from '../input-error';
import { Label } from '../label';
$:props = pluck($$props, ['title', 'name', 'placeholder']);
let className = '';
export { className as class };
export let id = '';
export let required = undefined;
export let disabled = false;
export let value = '';
export let label = '';
export let error = undefined;
export let info = undefined;
export let labelOnTheLeft = false;
export let element = undefined;
export let inputElement = undefined;
$:_id = id || name || guid();
const errorMessageId = guid();
</script>
1 change: 1 addition & 0 deletions src/input/input-time/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as InputTime } from './InputTime.svelte';
2 changes: 2 additions & 0 deletions tests/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { get } from 'svelte/store';
import { ANIMATION_SPEED } from '../../src';
import jest from 'jest-mock';

document.elementFromPoint = jest.fn().mockImplementation(() => ({ dataset: {} }));

document.scrollingElement = jest.fn().mockImplementation(() => ({
scrollTop: jest.fn(),
}));
Expand Down
6 changes: 5 additions & 1 deletion tests/input/InputRating.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,21 @@ test('InputRating', async () => {
expect(input).toHaveAttribute('aria-required');

const star2 = cmp.querySelector('.button:nth-child(2)');
expect(star2).toBeInTheDocument();
expect(star2).not.toHaveClass('active');

document.elementFromPoint = jest.fn().mockImplementation(() => ({ dataset: { star: '2' } }));
await fireEvent.mouseDown(star2);
await waitForTimeout();

expect(star2).toBeInTheDocument();
expect(star2).toHaveClass('active');
expect(mock).toHaveBeenCalled();

const star3 = cmp.querySelector('.button:nth-child(3)');
expect(star3).toBeInTheDocument();
expect(star3).not.toHaveClass('active');

document.elementFromPoint = jest.fn().mockImplementation(() => ({ dataset: { star: '3' } }));
await fireEvent.mouseDown(star3);
await waitForTimeout();
expect(star3).toHaveClass('active');
Expand Down
70 changes: 70 additions & 0 deletions tests/input/InputTime.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { render } from '@testing-library/svelte';
import jest from 'jest-mock';
import userEvent from '@testing-library/user-event';


import { InputTime } from '../../src/input';
import { waitForTimeout } from '../helpers/utils';


const props = {
id: 'Component1',
name: 'Component1',
title: 'Component1',
placeholder: 'Component1',
class: 'test-class',
required: true,
error: 'error',
label: 'Component1',
value: '12:00',
};


test('InputTime', async () => {
const { container, component, getByTitle } = render(InputTime, props);
const mock = jest.fn();
component.$on('change', mock);

const cmp = container.querySelector('.test-class');
expect(cmp).toBeInTheDocument();
expect(cmp).toHaveClass('test-class');

// verify props
const input = getByTitle(props.title);
expect(input).toHaveAttribute('id', props.id);
expect(input).toHaveAttribute('title', props.title);
expect(input).toHaveAttribute('name', props.name);
expect(input).toHaveAttribute('placeholder', props.placeholder);
expect(input).toHaveAttribute('aria-required');
expect(input).toHaveValue(props.value);

await userEvent.clear(input);
await userEvent.type(input, '14:00');
await userEvent.keyboard('[Enter]');
expect(input).toHaveValue('14:00');


let err = cmp.querySelector('.info-bar-error');
expect(err).toBeInTheDocument();
expect(err).toHaveTextContent(props.error);

await component.$set({ error: '' });
await waitForTimeout();
err = cmp.querySelector('.info-bar-error');
expect(err).not.toBeInTheDocument();

await component.$set({ info: 'info' });
let info = cmp.querySelector('.info-bar-info');
expect(info).toBeInTheDocument();
expect(info).toHaveTextContent('info');

await component.$set({ info: '' });
await waitForTimeout();
info = cmp.querySelector('.info-bar-info');
expect(info).not.toBeInTheDocument();

const lbl = cmp.querySelector('label');
expect(lbl).toBeInTheDocument();
expect(lbl).toHaveAttribute('for', props.id);
expect(lbl).toHaveTextContent(props.label);
});

0 comments on commit 029e1fd

Please sign in to comment.