Skip to content

Commit

Permalink
fix #UI-52
Browse files Browse the repository at this point in the history
  • Loading branch information
tborychowski committed Jul 13, 2023
1 parent 80a84d4 commit 96e76d9
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 176 deletions.
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Changelog
## v8.0.0 *(2023-07-?)*
- **Improved:** `info`, `error` and `label` attributes are now supported on other inputs (`Autocomplete`, `Datepicker`, `Select`, `ButtonToggle`, and `Toggle`).

### Breaking changes!
### Breaking changes

#### Autocomplete
- HTML structure changed `.autocomplete input` --> `.autocomplete .input-text-inner .input-text-row input`
Expand All @@ -15,6 +15,10 @@ Changelog
- HTML structure changed `.select-wrap select` --> `.select .input-text-inner .input-text-row select`


#### Toggle
- HTML structure changed from `.toggle .toggle-inner .toggle-scroller input` --> `.toggle .toggle-inner .toggle-label .toggle-scroller input`


----


Expand Down Expand Up @@ -46,7 +50,7 @@ Changelog
- **New:** `info`, `error` and `label` attributes are now supported on all basic inputs (`InputText`, `InputNumber`, `InputMath`, `InputPassword`, `Radio`, and `Checkbox`).
- **Improved:** `InputMath` component: support for `()` characters, to allow for more complex expressions.

### Breaking changes!
### Breaking changes

#### Checkbox
- HTML structure changed `input` --> `.checkbox .checkbox-row input`
Expand Down
30 changes: 24 additions & 6 deletions docs-src/components/toggle/Toggle.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,27 @@ The perfect toggle component in Svelte:
</ul>
<br>

<Toggle on:change="{onchange}" bind:value="{toggleValue}" /> {toggleValue}
<Toggle bind:value="{toggleValue}" /> {toggleValue}
<div class="toggle-box" class:visible="{toggleValue}">
<Toggle /> hidden initially<br>
<Toggle value="true"/> hidden initially
</div>
<br><br><br>

<h3>Disabled</h3>
<Toggle value="{true}" disabled /> (disabled)


<h3>Label</h3>
<Toggle label="Toggle the lights" />

<h3>Info</h3>
<Toggle label="Toggle the lights" info="This toggle switches the bathroom lights on/off" />

<h3>Error</h3>
<Toggle label="Toggle the lights" error="{error}" on:change="{onchange}"/>



<CodeExample html="{exampleHtml}" />

Expand All @@ -32,16 +44,19 @@ import { CodeExample } from '../../code-example';
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: 'name', type: 'string', description: 'Assign title to the underlying input.' },
{ name: 'required', description: 'Mark the input as <i>required</i> for form submission and effectively shows it as invalid, until filled.' },
{ name: 'id', type: 'string', description: 'Assign ID to the underlying input (if not set, a random string will be assigned).' },
{ name: 'info', type: 'string', description: 'Show info message above the toggle.' },
{ name: 'error', type: 'string', description: 'Error message to show above the toggle.' },
{ name: 'name', type: 'string', description: 'Assign name to the underlying input.' },
{ name: 'label', type: 'string', description: 'Label for the input.' },
{ name: 'required', description: 'Mark the input as <i>aria-required</i>.' },
{ name: 'title', type: 'string', description: 'Assign title to the underlying input.' },
{ name: 'value', type: 'string', description: 'Initial value of the input.' },
{ name: 'value', type: ['true', 'false'], description: 'Initial value of the toggle.' },
{ name: 'on:change', type: 'function', description: 'Triggered when the value changes.' },
];
const exampleHtml = `
<Toggle value="true" on:change="{onChange}" />
<Toggle value="true" label="Field label" on:change="{onChange}" />
<script>
function onChange (e) {
Expand All @@ -51,9 +66,12 @@ function onChange (e) {
`;
let error = 'I can\'t see anything now!';
let toggleValue = false;
function onchange (e) {
const val = e.detail;
error = val ? '' : 'I can\'t see anything now!';
console.log('onchange', e.detail);
}
</script>
8 changes: 6 additions & 2 deletions docs-src/pages/changelog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<ul>
<li><strong>Improved:</strong> <code>info</code>, <code>error</code> and <code>label</code> attributes are now supported on other inputs (<code>Autocomplete</code>, <code>Datepicker</code>, <code>Select</code>, <code>ButtonToggle</code>, and <code>Toggle</code>).</li>
</ul>
<h3>Breaking changes!</h3>
<h3>Breaking changes</h3>
<h4>Autocomplete</h4>
<ul>
<li>HTML structure changed <code>.autocomplete input</code> --&gt; <code>.autocomplete .input-text-inner .input-text-row input</code></li>
Expand All @@ -12,6 +12,10 @@
<ul>
<li>HTML structure changed <code>.select-wrap select</code> --&gt; <code>.select .input-text-inner .input-text-row select</code></li>
</ul>
<h4>Toggle</h4>
<ul>
<li>HTML structure changed from <code>.toggle .toggle-inner .toggle-scroller input</code> --&gt; <code>.toggle .toggle-inner .toggle-label .toggle-scroller input</code></li>
</ul>
<hr>
<h2>v7.1.2 <em>(2023-07-05)</em></h2>
<ul>
Expand Down Expand Up @@ -42,7 +46,7 @@
<li><strong>New:</strong> <code>info</code>, <code>error</code> and <code>label</code> attributes are now supported on all basic inputs (<code>InputText</code>, <code>InputNumber</code>, <code>InputMath</code>, <code>InputPassword</code>, <code>Radio</code>, and <code>Checkbox</code>).</li>
<li><strong>Improved:</strong> <code>InputMath</code> component: support for <code>()</code> characters, to allow for more complex expressions.</li>
</ul>
<h3>Breaking changes!</h3>
<h3>Breaking changes</h3>
<h4>Checkbox</h4>
<ul>
<li>HTML structure changed <code>input</code> --&gt; <code>.checkbox .checkbox-row input</code></li>
Expand Down
275 changes: 137 additions & 138 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.

24 changes: 16 additions & 8 deletions src/toggle/Toggle.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,35 @@
--toggle-height: calc(var(--toggle-width) / 2.5);
--knob-size: var(--toggle-height);

border-radius: var(--border-radius);
border: 1px solid var(--ui-color-text-dark-2);
background-color: var(--ui-color-background-dark-2);
padding: var(--toggle-padding);
display: inline-block;
display: inline-flex;
flex-flow: column;
align-items: flex-start;
position: relative;
vertical-align: middle;
-webkit-user-select: none;
user-select: none;

border-radius: var(--border-radius);
}

.toggle-inner {
border: 1px solid var(--ui-color-text-dark-2);
padding: var(--toggle-padding);
border-radius: inherit;
background-color: var(--ui-color-background-dark-2);
}

.toggle:has(:disabled) { pointer-events: none; opacity: 0.6; }
.toggle:focus {
outline: 1px solid transparent;

.toggle:focus { outline: 1px solid transparent; }
.toggle:focus .toggle-inner {
box-shadow: var(--ui-shadow-focus);
border-color: var(--ui-color-accent);
}

.toggle-input { display: none; }

.toggle-inner {
.toggle-label {
width: var(--toggle-width);
height: var(--toggle-height);
border-radius: calc(var(--border-radius) - var(--toggle-padding));
Expand Down
65 changes: 49 additions & 16 deletions src/toggle/Toggle.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div
class="toggle {className}"
class:checked="{value}"
class:has-error="{error}"
role="switch"
aria-checked="{value}"
tabindex="{disabled ? undefined : 0}"
Expand All @@ -10,35 +10,65 @@
on:mousedown={dragStart}
on:contextmenu|preventDefault
on:click|preventDefault>
<label class="toggle-inner" {title} bind:this="{label}">
<div class="toggle-scroller" bind:this="{scroller}">
<div class="toggle-option"></div>
<div class="toggle-handle" bind:this="{handle}"><div class="toggle-knob"></div></div>
<div class="toggle-option"></div>
<input {...inputProps} type="checkbox" class="toggle-input" bind:checked="{value}">
</div>
</label>

{#if label}
<label class="label" for="{_id}">{label}</label>
{/if}

<Info msg="{info}" />
<InputError id="{errorMessageId}" msg="{error}" />

<div class="toggle-inner">
<label class="toggle-label" {title}>
<div class="toggle-scroller" bind:this="{scroller}">
<div class="toggle-option"></div>
<div class="toggle-handle" bind:this="{handle}"><div class="toggle-knob"></div></div>
<div class="toggle-option"></div>
<input
class="toggle-input"
type="checkbox"
{disabled}
id="{_id}"
{name}
aria-invalid="{error}"
aria-errormessage="{error ? errorMessageId : undefined}"
aria-required="{required}"
bind:checked="{value}">
</div>
</label>
</div>
</div>

<script>
import { onMount, afterUpdate , createEventDispatcher } from 'svelte';
import { pluck } from '../utils';
import { Info, InputError } from '../info-bar';
import { guid } from '../utils';
import { getMouseX, isTouchDevice, initialMeasure } from './utils';
const dispatch = createEventDispatcher();
export let value = false;
export let disabled = undefined;
let className = '';
export { className as class };
export let id = '';
export let name = guid();
export let title = '';
export let required = undefined;
export let disabled = false;
export let label = '';
export let error = undefined;
export let info = undefined;
export let value = false;
let el, label, scroller, handle, startX, currentX = 0;
$:_id = id || name || guid();
const errorMessageId = guid();
let el, scroller, handle, startX, currentX = 0;
let scrollerStartX, scrollerEndX, handleStartX;
let isClick = false, isDragging = false;
let oldValue;
$:title = $$props.title;
$:inputProps = pluck($$props, ['id', 'name', 'disabled', 'required']);
onMount(() => {
toggleTransitions(false);
Expand Down Expand Up @@ -74,6 +104,9 @@ function onKey (e) {
function dragStart (e) {
const target = e.target;
if (!target.closest('.toggle-inner, .toggle>label')) return;
// prevent double call
if (isTouchDevice && e.type !== 'touchstart') return;
Expand Down
34 changes: 32 additions & 2 deletions tests/ButtonToggle.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ButtonToggle } from '../src/button-toggle';
import { render, fireEvent } from '@testing-library/svelte';
import { waitForTimeout } from './helpers/utils';


test('ButtonToggle', async () => {
Expand All @@ -14,10 +15,14 @@ test('ButtonToggle', async () => {
name: 'Component1',
class: 'test-class',
items,
value
value,
error: 'error',
label: 'Component1',
};

const { container, component } = render(ButtonToggle, props);
const cmp = container.querySelector('.test-class');

const btnGroup = container.querySelector('.test-class');

await component.$set({ round: true });
Expand All @@ -27,7 +32,7 @@ test('ButtonToggle', async () => {
expect(btnGroup).toHaveClass('test-class');


const labelButtons = btnGroup.querySelectorAll('label');
const labelButtons = btnGroup.querySelectorAll('.input-text-inner label');
const inputs = btnGroup.querySelectorAll('label input');
expect(inputs.length).toBe(items.length);

Expand All @@ -44,4 +49,29 @@ test('ButtonToggle', async () => {

await fireEvent.click(labelButtons[1]);
expect(inputs[1]).toBeChecked();

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);

});
30 changes: 29 additions & 1 deletion tests/Toggle.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Toggle } from '../src/toggle';
import { render, fireEvent } from '@testing-library/svelte';
import { waitForTimeout } from './helpers/utils';

test('Toggle', async () => {
const props = {
Expand All @@ -8,6 +9,8 @@ test('Toggle', async () => {
title: 'Component1',
class: 'test-class',
required: true,
error: 'error',
label: 'Component1',
};
const { container, getByTitle, component } = render(Toggle, props);

Expand All @@ -25,12 +28,37 @@ test('Toggle', async () => {
const input = label.querySelector('input');
expect(input).toHaveAttribute('id', 'Component1');
expect(input).toHaveAttribute('name', 'Component1');
expect(input).toHaveAttribute('required');
expect(input).toHaveAttribute('aria-required');
expect(input).not.toBeChecked();

await fireEvent.mouseDown(label);
await fireEvent.mouseUp(label);
expect(mock).toHaveBeenCalled();
expect(input).toBeChecked();


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 96e76d9

Please sign in to comment.