@internetarchive/collection-browser 0.4.10 → 0.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/collection-browser.d.ts +1 -0
- package/dist/src/collection-browser.js +24 -5
- package/dist/src/collection-browser.js.map +1 -1
- package/dist/src/collection-facets/more-facets-content.d.ts +3 -2
- package/dist/src/collection-facets/more-facets-content.js +24 -47
- package/dist/src/collection-facets/more-facets-content.js.map +1 -1
- package/dist/src/collection-facets/more-facets-pagination.js +5 -3
- package/dist/src/collection-facets/more-facets-pagination.js.map +1 -1
- package/dist/src/collection-facets/toggle-switch.d.ts +41 -0
- package/dist/src/collection-facets/toggle-switch.js +184 -0
- package/dist/src/collection-facets/toggle-switch.js.map +1 -0
- package/dist/src/empty-placeholder.d.ts +5 -3
- package/dist/src/empty-placeholder.js +39 -3
- package/dist/src/empty-placeholder.js.map +1 -1
- package/dist/src/sort-filter-bar/alpha-bar.js +8 -0
- package/dist/src/sort-filter-bar/alpha-bar.js.map +1 -1
- package/dist/src/tiles/list/tile-list-compact.d.ts +1 -0
- package/dist/src/tiles/list/tile-list-compact.js +15 -2
- package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
- package/dist/src/tiles/list/tile-list.js +3 -0
- package/dist/src/tiles/list/tile-list.js.map +1 -1
- package/dist/test/collection-facets/toggle-switch.test.d.ts +1 -0
- package/dist/test/collection-facets/toggle-switch.test.js +87 -0
- package/dist/test/collection-facets/toggle-switch.test.js.map +1 -0
- package/dist/test/tiles/list/tile-list-compact.test.js +12 -0
- package/dist/test/tiles/list/tile-list-compact.test.js.map +1 -1
- package/dist/test/tiles/list/tile-list.test.js +12 -0
- package/dist/test/tiles/list/tile-list.test.js.map +1 -1
- package/package.json +3 -3
- package/src/collection-browser.ts +25 -1
- package/src/collection-facets/more-facets-content.ts +25 -48
- package/src/collection-facets/more-facets-pagination.ts +5 -3
- package/src/collection-facets/toggle-switch.ts +184 -0
- package/src/empty-placeholder.ts +53 -7
- package/src/sort-filter-bar/alpha-bar.ts +8 -0
- package/src/tiles/list/tile-list-compact.ts +15 -2
- package/src/tiles/list/tile-list.ts +3 -0
- package/test/collection-facets/toggle-switch.test.ts +154 -0
- package/test/tiles/list/tile-list-compact.test.ts +14 -0
- package/test/tiles/list/tile-list.test.ts +14 -0
package/src/empty-placeholder.ts
CHANGED
|
@@ -1,22 +1,35 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
css,
|
|
3
|
+
html,
|
|
4
|
+
LitElement,
|
|
5
|
+
CSSResultGroup,
|
|
6
|
+
nothing,
|
|
7
|
+
TemplateResult,
|
|
8
|
+
} from 'lit';
|
|
2
9
|
import { customElement, property } from 'lit/decorators.js';
|
|
3
10
|
import { choose } from 'lit/directives/choose.js';
|
|
4
11
|
|
|
5
12
|
import emptyQueryIcon from './assets/img/icons/empty-query';
|
|
6
13
|
import nullResultIcon from './assets/img/icons/null-result';
|
|
7
14
|
|
|
8
|
-
export type PlaceholderType =
|
|
15
|
+
export type PlaceholderType =
|
|
16
|
+
| 'empty-query'
|
|
17
|
+
| 'null-result'
|
|
18
|
+
| 'query-error'
|
|
19
|
+
| null;
|
|
9
20
|
@customElement('empty-placeholder')
|
|
10
21
|
export class EmptyPlaceholder extends LitElement {
|
|
11
22
|
@property({ type: String }) placeholderType: PlaceholderType = null;
|
|
12
23
|
|
|
13
24
|
@property({ type: Boolean }) isMobileView?: false;
|
|
14
25
|
|
|
26
|
+
@property({ type: String }) detailMessage?: string = '';
|
|
27
|
+
|
|
15
28
|
render() {
|
|
16
29
|
return this.placeholderType ? html`${this.placeholderTemplate}` : nothing;
|
|
17
30
|
}
|
|
18
31
|
|
|
19
|
-
private get placeholderTemplate() {
|
|
32
|
+
private get placeholderTemplate(): TemplateResult {
|
|
20
33
|
return html`
|
|
21
34
|
<div
|
|
22
35
|
class="placeholder ${this.placeholderType} ${this.isMobileView
|
|
@@ -26,12 +39,13 @@ export class EmptyPlaceholder extends LitElement {
|
|
|
26
39
|
${choose(this.placeholderType, [
|
|
27
40
|
['empty-query', () => this.emptyQueryTemplate],
|
|
28
41
|
['null-result', () => this.nullResultTemplate],
|
|
42
|
+
['query-error', () => this.queryErrorTemplate],
|
|
29
43
|
])}
|
|
30
44
|
</div>
|
|
31
45
|
`;
|
|
32
46
|
}
|
|
33
47
|
|
|
34
|
-
private get emptyQueryTemplate() {
|
|
48
|
+
private get emptyQueryTemplate(): TemplateResult {
|
|
35
49
|
return html`
|
|
36
50
|
<h2 class="title">
|
|
37
51
|
To begin searching, enter a search term in the box above and hit "Go".
|
|
@@ -40,7 +54,7 @@ export class EmptyPlaceholder extends LitElement {
|
|
|
40
54
|
`;
|
|
41
55
|
}
|
|
42
56
|
|
|
43
|
-
private get nullResultTemplate() {
|
|
57
|
+
private get nullResultTemplate(): TemplateResult {
|
|
44
58
|
return html`
|
|
45
59
|
<h2 class="title">
|
|
46
60
|
Your search did not match any items in the Archive. Try different
|
|
@@ -50,6 +64,22 @@ export class EmptyPlaceholder extends LitElement {
|
|
|
50
64
|
`;
|
|
51
65
|
}
|
|
52
66
|
|
|
67
|
+
private get queryErrorTemplate(): TemplateResult {
|
|
68
|
+
return html`
|
|
69
|
+
<h2 class="title">
|
|
70
|
+
The search engine encountered an error, which might be related to your
|
|
71
|
+
search query.
|
|
72
|
+
<a
|
|
73
|
+
href="https://help.archive.org/help/search-building-powerful-complex-queries/"
|
|
74
|
+
>
|
|
75
|
+
Tips for constructing search queries.
|
|
76
|
+
</a>
|
|
77
|
+
</h2>
|
|
78
|
+
<div>${nullResultIcon}</div>
|
|
79
|
+
<p class="error-details">Error details: ${this.detailMessage}</p>
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
|
|
53
83
|
static get styles(): CSSResultGroup {
|
|
54
84
|
return css`
|
|
55
85
|
:host {
|
|
@@ -57,6 +87,16 @@ export class EmptyPlaceholder extends LitElement {
|
|
|
57
87
|
width: 100%;
|
|
58
88
|
}
|
|
59
89
|
|
|
90
|
+
a {
|
|
91
|
+
text-decoration: none;
|
|
92
|
+
}
|
|
93
|
+
a:link {
|
|
94
|
+
color: var(--ia-theme-link-color, #4b64ff);
|
|
95
|
+
}
|
|
96
|
+
a:hover {
|
|
97
|
+
text-decoration: underline;
|
|
98
|
+
}
|
|
99
|
+
|
|
60
100
|
.placeholder {
|
|
61
101
|
display: block;
|
|
62
102
|
}
|
|
@@ -64,16 +104,22 @@ export class EmptyPlaceholder extends LitElement {
|
|
|
64
104
|
.desktop svg {
|
|
65
105
|
max-height: 40rem;
|
|
66
106
|
}
|
|
67
|
-
.desktop .title
|
|
107
|
+
.desktop .title,
|
|
108
|
+
.desktop .error-details {
|
|
68
109
|
margin: 4rem 0;
|
|
69
110
|
}
|
|
70
111
|
|
|
71
112
|
.mobile svg {
|
|
72
113
|
max-height: 20rem;
|
|
73
114
|
}
|
|
74
|
-
.mobile .title
|
|
115
|
+
.mobile .title,
|
|
116
|
+
.mobile .error-details {
|
|
75
117
|
margin: 2rem 0.5;
|
|
76
118
|
}
|
|
119
|
+
|
|
120
|
+
.error-details {
|
|
121
|
+
font-size: 1.2rem;
|
|
122
|
+
}
|
|
77
123
|
`;
|
|
78
124
|
}
|
|
79
125
|
}
|
|
@@ -233,5 +233,13 @@ export class AlphaBar extends LitElement {
|
|
|
233
233
|
alpha-bar-tooltip.fade-in {
|
|
234
234
|
opacity: 1;
|
|
235
235
|
}
|
|
236
|
+
|
|
237
|
+
/* Make alphabet bar a 2-row container in small screen widths */
|
|
238
|
+
@media screen and (max-width: 768px) {
|
|
239
|
+
ul {
|
|
240
|
+
display: grid;
|
|
241
|
+
grid-template-columns: repeat(13, 1fr);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
236
244
|
`;
|
|
237
245
|
}
|
|
@@ -41,7 +41,9 @@ export class TileListCompact extends LitElement {
|
|
|
41
41
|
.loggedIn=${this.loggedIn}
|
|
42
42
|
>
|
|
43
43
|
</image-block>
|
|
44
|
-
<
|
|
44
|
+
<a href=${this.href} id="title"
|
|
45
|
+
>${DOMPurify.sanitize(this.model?.title ?? '')}</a
|
|
46
|
+
>
|
|
45
47
|
<div id="creator">
|
|
46
48
|
${this.model?.mediatype === 'account'
|
|
47
49
|
? accountLabel(this.model?.dateAdded)
|
|
@@ -60,6 +62,14 @@ export class TileListCompact extends LitElement {
|
|
|
60
62
|
`;
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
private get href(): string {
|
|
66
|
+
// Use the server-specified href if available.
|
|
67
|
+
// Otherwise, construct a details page URL from the item identifier.
|
|
68
|
+
return this.model?.href
|
|
69
|
+
? `${this.baseNavigationUrl}${this.model.href}`
|
|
70
|
+
: `${this.baseNavigationUrl}/details/${this.model?.identifier}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
63
73
|
/*
|
|
64
74
|
* TODO: fix field names to match model in src/collection-browser.ts
|
|
65
75
|
* private get dateSortSelector()
|
|
@@ -145,10 +155,13 @@ export class TileListCompact extends LitElement {
|
|
|
145
155
|
}
|
|
146
156
|
|
|
147
157
|
#title {
|
|
148
|
-
color: #4b64ff;
|
|
149
158
|
text-decoration: none;
|
|
150
159
|
}
|
|
151
160
|
|
|
161
|
+
#title:link {
|
|
162
|
+
color: var(--ia-theme-link-color, #4b64ff);
|
|
163
|
+
}
|
|
164
|
+
|
|
152
165
|
#title,
|
|
153
166
|
#creator {
|
|
154
167
|
text-overflow: ellipsis;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { expect, fixture } from '@open-wc/testing';
|
|
2
|
+
import sinon from 'sinon';
|
|
3
|
+
import { html } from 'lit';
|
|
4
|
+
import type { ToggleSwitch } from '../../src/collection-facets/toggle-switch';
|
|
5
|
+
|
|
6
|
+
import '../../src/collection-facets/toggle-switch';
|
|
7
|
+
|
|
8
|
+
describe('Toggle switch', () => {
|
|
9
|
+
it('renders component', async () => {
|
|
10
|
+
const el = await fixture<ToggleSwitch>(
|
|
11
|
+
html`<toggle-switch></toggle-switch>`
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
expect(el.shadowRoot?.querySelector('#switch-button')).to.exist;
|
|
15
|
+
expect(el.shadowRoot?.querySelector('#knob')).to.exist;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('renders provided L/R values', async () => {
|
|
19
|
+
const el = await fixture<ToggleSwitch>(
|
|
20
|
+
html`<toggle-switch .leftValue=${'L'} .rightValue=${'R'}></toggle-switch>`
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
expect(el.value).to.equal('L');
|
|
24
|
+
expect(el.selectedLabel).to.equal('L');
|
|
25
|
+
|
|
26
|
+
expect(
|
|
27
|
+
el.shadowRoot
|
|
28
|
+
?.querySelector('label[for=switch-left]')
|
|
29
|
+
?.textContent?.trim()
|
|
30
|
+
).to.equal('L');
|
|
31
|
+
expect(
|
|
32
|
+
el.shadowRoot
|
|
33
|
+
?.querySelector('label[for=switch-right]')
|
|
34
|
+
?.textContent?.trim()
|
|
35
|
+
).to.equal('R');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('renders provided L/R labels instead of values', async () => {
|
|
39
|
+
const el = await fixture<ToggleSwitch>(
|
|
40
|
+
html`<toggle-switch
|
|
41
|
+
.leftValue=${'L'}
|
|
42
|
+
.leftLabel=${'L-label'}
|
|
43
|
+
.rightValue=${'R'}
|
|
44
|
+
.rightLabel=${'R-label'}
|
|
45
|
+
></toggle-switch>`
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(el.value).to.equal('L');
|
|
49
|
+
expect(el.selectedLabel).to.equal('L-label');
|
|
50
|
+
|
|
51
|
+
expect(
|
|
52
|
+
(el.shadowRoot?.querySelector('#switch-left') as HTMLInputElement)?.value
|
|
53
|
+
).to.equal('L');
|
|
54
|
+
expect(
|
|
55
|
+
(el.shadowRoot?.querySelector('#switch-right') as HTMLInputElement)?.value
|
|
56
|
+
).to.equal('R');
|
|
57
|
+
|
|
58
|
+
expect(
|
|
59
|
+
el.shadowRoot
|
|
60
|
+
?.querySelector('label[for=switch-left]')
|
|
61
|
+
?.textContent?.trim()
|
|
62
|
+
).to.equal('L-label');
|
|
63
|
+
expect(
|
|
64
|
+
el.shadowRoot
|
|
65
|
+
?.querySelector('label[for=switch-right]')
|
|
66
|
+
?.textContent?.trim()
|
|
67
|
+
).to.equal('R-label');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('sets the initial side', async () => {
|
|
71
|
+
const el = await fixture<ToggleSwitch>(
|
|
72
|
+
html`<toggle-switch
|
|
73
|
+
.leftValue=${'L'}
|
|
74
|
+
.rightValue=${'R'}
|
|
75
|
+
.side=${'right'}
|
|
76
|
+
></toggle-switch>`
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(el.value).to.equal('R');
|
|
80
|
+
expect(el.selectedLabel).to.equal('R');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('toggles on click', async () => {
|
|
84
|
+
const el = await fixture<ToggleSwitch>(
|
|
85
|
+
html`<toggle-switch .leftValue=${'L'} .rightValue=${'R'}></toggle-switch>`
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const button = el.shadowRoot?.querySelector(
|
|
89
|
+
'#switch-button'
|
|
90
|
+
) as HTMLButtonElement;
|
|
91
|
+
|
|
92
|
+
expect(el.value).to.equal('L');
|
|
93
|
+
button.click();
|
|
94
|
+
await el.updateComplete;
|
|
95
|
+
|
|
96
|
+
expect(el.value).to.equal('R');
|
|
97
|
+
button.click();
|
|
98
|
+
await el.updateComplete;
|
|
99
|
+
|
|
100
|
+
expect(el.value).to.equal('L');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('toggles on radio change', async () => {
|
|
104
|
+
const el = await fixture<ToggleSwitch>(
|
|
105
|
+
html`<toggle-switch .leftValue=${'L'} .rightValue=${'R'}></toggle-switch>`
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const leftRadio = el.shadowRoot?.querySelector(
|
|
109
|
+
'#switch-left'
|
|
110
|
+
) as HTMLInputElement;
|
|
111
|
+
const rightRadio = el.shadowRoot?.querySelector(
|
|
112
|
+
'#switch-right'
|
|
113
|
+
) as HTMLInputElement;
|
|
114
|
+
|
|
115
|
+
expect(el.value).to.equal('L');
|
|
116
|
+
rightRadio.click();
|
|
117
|
+
await el.updateComplete;
|
|
118
|
+
|
|
119
|
+
expect(el.value).to.equal('R');
|
|
120
|
+
leftRadio.click();
|
|
121
|
+
await el.updateComplete;
|
|
122
|
+
|
|
123
|
+
expect(el.value).to.equal('L');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('emits change event when toggled', async () => {
|
|
127
|
+
const changeSpy = sinon.spy();
|
|
128
|
+
const el = await fixture<ToggleSwitch>(
|
|
129
|
+
html`<toggle-switch
|
|
130
|
+
.leftValue=${'L'}
|
|
131
|
+
.rightValue=${'R'}
|
|
132
|
+
@change=${changeSpy}
|
|
133
|
+
></toggle-switch>`
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const button = el.shadowRoot?.querySelector(
|
|
137
|
+
'#switch-button'
|
|
138
|
+
) as HTMLButtonElement;
|
|
139
|
+
|
|
140
|
+
button.click();
|
|
141
|
+
await el.updateComplete;
|
|
142
|
+
|
|
143
|
+
expect(changeSpy.callCount).to.equal(1);
|
|
144
|
+
|
|
145
|
+
const leftRadio = el.shadowRoot?.querySelector(
|
|
146
|
+
'#switch-left'
|
|
147
|
+
) as HTMLInputElement;
|
|
148
|
+
|
|
149
|
+
leftRadio.click();
|
|
150
|
+
await el.updateComplete;
|
|
151
|
+
|
|
152
|
+
expect(changeSpy.callCount).to.equal(2);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -37,6 +37,20 @@ describe('List Tile Compact', () => {
|
|
|
37
37
|
expect(creator).to.exist;
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
it('should render title link with model href if provided', async () => {
|
|
41
|
+
const el = await fixture<TileListCompact>(html`
|
|
42
|
+
<tile-list-compact
|
|
43
|
+
.baseNavigationUrl=${''}
|
|
44
|
+
.model=${{ title: 'foo', href: '/foo/bar' }}
|
|
45
|
+
></tile-list-compact>
|
|
46
|
+
`);
|
|
47
|
+
|
|
48
|
+
const title = el.shadowRoot?.querySelector('#title');
|
|
49
|
+
|
|
50
|
+
expect(title).to.exist;
|
|
51
|
+
expect(title?.getAttribute('href')).to.equal('/foo/bar');
|
|
52
|
+
});
|
|
53
|
+
|
|
40
54
|
it('should render weekly views when sorting by week', async () => {
|
|
41
55
|
const el = await fixture<TileListCompact>(html`
|
|
42
56
|
<tile-list-compact
|
|
@@ -37,6 +37,20 @@ describe('List Tile', () => {
|
|
|
37
37
|
expect(bottomLine).to.exist;
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
it('should render title link with model href if provided', async () => {
|
|
41
|
+
const el = await fixture<TileList>(html`
|
|
42
|
+
<tile-list
|
|
43
|
+
.baseNavigationUrl=${''}
|
|
44
|
+
.model=${{ title: 'foo', href: '/foo/bar' }}
|
|
45
|
+
></tile-list>
|
|
46
|
+
`);
|
|
47
|
+
|
|
48
|
+
const title = el.shadowRoot?.querySelector('#title > a');
|
|
49
|
+
|
|
50
|
+
expect(title).to.exist;
|
|
51
|
+
expect(title?.getAttribute('href')).to.equal('/foo/bar');
|
|
52
|
+
});
|
|
53
|
+
|
|
40
54
|
it('should render with creator element but not dates', async () => {
|
|
41
55
|
const el = await fixture<TileList>(html`
|
|
42
56
|
<tile-list .model=${{ creators: ['someone'] }}></tile-list>
|