-
Notifications
You must be signed in to change notification settings - Fork 880
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[lit-html] Add when, map, join, and range directives. (#2335)
- Loading branch information
1 parent
49ecf62
commit d319cf5
Showing
18 changed files
with
432 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'lit-html': minor | ||
--- | ||
|
||
Add `when`, `map`, `join`, and `range` directives. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/** | ||
* @license | ||
* Copyright 2021 Google LLC | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
|
||
/** | ||
* Returns an iterable containing the values in `items` interleaved with the | ||
* `joiner` value. | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* render() { | ||
* return html` | ||
* ${join(items, html`<span class="separator">|</span>`)} | ||
* `; | ||
* } | ||
*/ | ||
export function join<I, J>( | ||
items: Iterable<I> | undefined, | ||
joiner: (index: number) => J | ||
): Iterable<I | J>; | ||
export function join<I, J>( | ||
items: Iterable<I> | undefined, | ||
joiner: J | ||
): Iterable<I | J>; | ||
export function* join<I, J>(items: Iterable<I> | undefined, joiner: J) { | ||
const isFunction = typeof joiner === 'function'; | ||
if (items !== undefined) { | ||
let i = -1; | ||
for (const value of items) { | ||
if (i > -1) { | ||
yield isFunction ? joiner(i) : joiner; | ||
} | ||
i++; | ||
yield value; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/** | ||
* @license | ||
* Copyright 2021 Google LLC | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
|
||
/** | ||
* Returns an iterable containing the result of calling `f(value)` on each | ||
* value in `items`. | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* render() { | ||
* return html` | ||
* <ul> | ||
* ${map(items, (i) => html`<li>${i}</li>`)} | ||
* </ul> | ||
* `; | ||
* } | ||
* ``` | ||
*/ | ||
export function* map<T>( | ||
items: Iterable<T> | undefined, | ||
f: (value: T, index: number) => unknown | ||
) { | ||
if (items !== undefined) { | ||
let i = 0; | ||
for (const value of items) { | ||
yield f(value, i++); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/** | ||
* @license | ||
* Copyright 2021 Google LLC | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
|
||
/** | ||
* Returns an iterable of integers from `start` to `end` (exclusive) | ||
* incrementing by `step`. | ||
* | ||
* If `start` is omitted, the range starts at `0`. `step` defaults to `1`. | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* render() { | ||
* return html` | ||
* ${map(range(8), () => html`<div class="cell"></div>`)} | ||
* `; | ||
* } | ||
* ``` | ||
*/ | ||
export function range(end: number): Iterable<number>; | ||
export function range( | ||
start: number, | ||
end: number, | ||
step?: number | ||
): Iterable<number>; | ||
export function* range(startOrEnd: number, end?: number, step = 1) { | ||
const start = end === undefined ? 0 : startOrEnd; | ||
end ??= startOrEnd; | ||
for (let i = start; step > 0 ? i < end : end < i; i += step) { | ||
yield i; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/** | ||
* @license | ||
* Copyright 2021 Google LLC | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
|
||
/** | ||
* When `condition` is true, returns the result of calling `trueCase()`, else | ||
* returns the result of calling `falseCase()` if `falseCase` is defined. | ||
* | ||
* This is a convenience wrapper around a ternary expression that makes it a | ||
* little nicer to write an inline conditional without an else. | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* render() { | ||
* return html` | ||
* ${when(this.user, () => html`User: ${this.user.username}`, () => html`Sign In...`)} | ||
* `; | ||
* } | ||
* ``` | ||
*/ | ||
export function when<T, F>( | ||
condition: true, | ||
trueCase: () => T, | ||
falseCase?: () => F | ||
): T; | ||
export function when<T, F = undefined>( | ||
condition: false, | ||
trueCase: () => T, | ||
falseCase?: () => F | ||
): F; | ||
export function when<T, F = undefined>( | ||
condition: unknown, | ||
trueCase: () => T, | ||
falseCase?: () => F | ||
): T | F; | ||
export function when( | ||
condition: unknown, | ||
trueCase: () => unknown, | ||
falseCase?: () => unknown | ||
): unknown { | ||
return condition ? trueCase() : falseCase?.(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/** | ||
* @license | ||
* Copyright 2021 Google LLC | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
import {html} from '../../lit-html.js'; | ||
import {makeAssertRender} from '../test-utils/assert-render.js'; | ||
|
||
import {join} from '../../directives/join.js'; | ||
|
||
suite('join', () => { | ||
let container: HTMLDivElement; | ||
|
||
const assertRender = makeAssertRender(() => container); | ||
|
||
setup(() => { | ||
container = document.createElement('div'); | ||
}); | ||
|
||
test('with array', () => { | ||
assertRender(join(['a', 'b', 'c'], ','), 'a,b,c'); | ||
}); | ||
|
||
test('with empty array', () => { | ||
assertRender(join([], ','), ''); | ||
}); | ||
|
||
test('with undefined', () => { | ||
assertRender(join(undefined, ','), ''); | ||
}); | ||
|
||
test('with iterable', () => { | ||
function* iterate<T>(items: Array<T>) { | ||
for (const i of items) { | ||
yield i; | ||
} | ||
} | ||
assertRender(join(iterate(['a', 'b', 'c']), ','), 'a,b,c'); | ||
}); | ||
|
||
test('passes index', () => { | ||
assertRender( | ||
join(['a', 'b', 'c'], (i) => html`<p>${i}</p>`), | ||
'a<p>0</p>b<p>1</p>c' | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/** | ||
* @license | ||
* Copyright 2021 Google LLC | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
import {html} from '../../lit-html.js'; | ||
import {makeAssertRender} from '../test-utils/assert-render.js'; | ||
|
||
import {map} from '../../directives/map.js'; | ||
|
||
suite('map', () => { | ||
let container: HTMLDivElement; | ||
|
||
const assertRender = makeAssertRender(() => container); | ||
|
||
setup(() => { | ||
container = document.createElement('div'); | ||
}); | ||
test('with array', () => { | ||
assertRender( | ||
map(['a', 'b', 'c'], (v) => html`<p>${v}</p>`), | ||
'<p>a</p><p>b</p><p>c</p>' | ||
); | ||
}); | ||
|
||
test('with empty array', () => { | ||
assertRender( | ||
map([], (v) => html`<p>${v}</p>`), | ||
'' | ||
); | ||
}); | ||
|
||
test('with undefined', () => { | ||
assertRender( | ||
map(undefined, (v) => html`<p>${v}</p>`), | ||
'' | ||
); | ||
}); | ||
|
||
test('with iterable', () => { | ||
function* iterate<T>(items: Array<T>) { | ||
for (const i of items) { | ||
yield i; | ||
} | ||
} | ||
assertRender( | ||
map(iterate(['a', 'b', 'c']), (v) => html`<p>${v}</p>`), | ||
'<p>a</p><p>b</p><p>c</p>' | ||
); | ||
}); | ||
|
||
test('passes index', () => { | ||
assertRender( | ||
map(['a', 'b', 'c'], (v, i) => html`<p>${v}:${i}</p>`), | ||
'<p>a:0</p><p>b:1</p><p>c:2</p>' | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/** | ||
* @license | ||
* Copyright 2021 Google LLC | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
import {range} from '../../directives/range.js'; | ||
import {assert} from '@esm-bundle/chai'; | ||
|
||
suite('range', () => { | ||
test('positive end', () => { | ||
assert.deepEqual([...range(0)], []); | ||
assert.deepEqual([...range(3)], [0, 1, 2]); | ||
}); | ||
|
||
test('start and end', () => { | ||
assert.deepEqual([...range(0, 3)], [0, 1, 2]); | ||
assert.deepEqual([...range(-1, 1)], [-1, 0]); | ||
assert.deepEqual([...range(-2, -1)], [-2]); | ||
}); | ||
|
||
test('end < start', () => { | ||
// This case checks that we don't cause an infinite loop | ||
assert.deepEqual([...range(2, 1)], []); | ||
}); | ||
|
||
test('custom step', () => { | ||
assert.deepEqual([...range(0, 10, 3)], [0, 3, 6, 9]); | ||
}); | ||
|
||
test('negative step', () => { | ||
assert.deepEqual([...range(0, -3, -1)], [0, -1, -2]); | ||
// This case checks that we don't cause an infinite loop | ||
assert.deepEqual([...range(0, 10, -1)], []); | ||
}); | ||
}); |
Oops, something went wrong.